Clean up some version dependencies
[yaffs2.git] / yaffs_vfs_multi.c
index 0252211dd3760ad4ce01b17113f53befd7dcc42f..e2f002e0cac9020f43d425d582d6e486d21ce503 100644 (file)
 #define YAFFS_HAS_EVICT_INODE
 #endif
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13))
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) && \
+    (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
 #define YAFFS_NEW_FOLLOW_LINK 1
 #else
 #define YAFFS_NEW_FOLLOW_LINK 0
 #endif
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+#define YAFFS_HAS_WRITE_SUPER
+#endif
+
+
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
 #include <linux/config.h>
 #endif
@@ -171,8 +177,14 @@ static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size)
 #include "yaffs_linux.h"
 
 #include "yaffs_mtdif.h"
+#include "yaffs_packedtags2.h"
+#include "yaffs_getblockinfo.h"
+
+unsigned int yaffs_trace_mask =
+               YAFFS_TRACE_BAD_BLOCKS |
+               YAFFS_TRACE_ALWAYS |
+               0;
 
-unsigned int yaffs_trace_mask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS;
 unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS;
 unsigned int yaffs_auto_checkpoint = 1;
 unsigned int yaffs_gc_control = 1;
@@ -225,11 +237,46 @@ MODULE_PARM(yaffs_gc_control, "i");
 #define Y_CLEAR_INODE(i) end_writeback(i)
 #endif
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+#define YAFFS_USE_DIR_ITERATE
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
+#define YAFFS_USE_XATTR
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0))
+#define YAFFS_NEW_PROCFS
+#include <linux/seq_file.h>
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+#define PAGE_CACHE_SIZE PAGE_SIZE
+#define PAGE_CACHE_SHIFT PAGE_SHIFT
+#define Y_GET_DENTRY(f) ((f)->f_path.dentry)
+#define YAFFS_NEW_XATTR 1
+#define YAFFS_NEW_GET_LINK 1
+#else
+#define Y_GET_DENTRY(f) ((f)->f_dentry)
+#define YAFFS_NEW_XATTR 0
+#define YAFFS_NEW_GET_LINK 0
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
+#define page_cache_release put_page
+#endif
 
 #define update_dir_time(dir) do {\
                        (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \
                } while (0)
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
+static inline int setattr_prepare(struct dentry *dentry, struct iattr *attr)
+{
+       return inode_change_ok(dentry->d_inode, attr);
+}
+#endif
+
 static void yaffs_fill_inode_from_obj(struct inode *inode,
                                      struct yaffs_obj *obj);
 
@@ -255,15 +302,15 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg)
        struct yaffs_obj *obj;
        unsigned char *pg_buf;
        int ret;
-       loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT;
+       loff_t pos = ((loff_t) pg->index) << PAGE_SHIFT;
        struct yaffs_dev *dev;
 
        yaffs_trace(YAFFS_TRACE_OS,
                "yaffs_readpage_nolock at %lld, size %08x",
                (long long)pos,
-               (unsigned)PAGE_CACHE_SIZE);
+               (unsigned)PAGE_SIZE);
 
-       obj = yaffs_dentry_to_obj(f->f_dentry);
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
 
        dev = obj->my_dev;
 
@@ -461,7 +508,7 @@ static ssize_t yaffs_hold_space(struct file *f)
 
        int n_free_chunks;
 
-       obj = yaffs_dentry_to_obj(f->f_dentry);
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
 
        dev = obj->my_dev;
 
@@ -479,7 +526,7 @@ static void yaffs_release_space(struct file *f)
        struct yaffs_obj *obj;
        struct yaffs_dev *dev;
 
-       obj = yaffs_dentry_to_obj(f->f_dentry);
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
 
        dev = obj->my_dev;
 
@@ -571,7 +618,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
        struct inode *inode;
        struct yaffs_dev *dev;
 
-       obj = yaffs_dentry_to_obj(f->f_dentry);
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
 
        if (!obj) {
                yaffs_trace(YAFFS_TRACE_OS,
@@ -583,7 +630,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n,
 
        yaffs_gross_lock(dev);
 
-       inode = f->f_dentry->d_inode;
+       inode = Y_GET_DENTRY(f)->d_inode;
 
        if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
                ipos = inode->i_size;
@@ -707,7 +754,7 @@ static int yaffs_file_flush(struct file *file, fl_owner_t id)
 static int yaffs_file_flush(struct file *file)
 #endif
 {
-       struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry);
+       struct yaffs_obj *obj = yaffs_dentry_to_obj(Y_GET_DENTRY(file));
 
        struct yaffs_dev *dev = obj->my_dev;
 
@@ -718,7 +765,7 @@ static int yaffs_file_flush(struct file *file)
 
        yaffs_gross_lock(dev);
 
-       yaffs_flush_file(obj, 1, 0);
+       yaffs_flush_file(obj, 1, 0, 0);
 
        yaffs_gross_unlock(dev);
 
@@ -748,7 +795,7 @@ static int yaffs_sync_object(struct file *file, struct dentry *dentry,
        yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC,
                "yaffs_sync_object");
        yaffs_gross_lock(dev);
-       yaffs_flush_file(obj, 1, datasync);
+       yaffs_flush_file(obj, 1, datasync, 0);
        yaffs_gross_unlock(dev);
        return 0;
 }
@@ -756,15 +803,28 @@ static int yaffs_sync_object(struct file *file, struct dentry *dentry,
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
 static const struct file_operations yaffs_file_operations = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
+       .read = new_sync_read,
+       .write = new_sync_write,
+#endif
+       .read_iter = generic_file_read_iter,
+       .write_iter = generic_file_write_iter,
+#else
        .read = do_sync_read,
        .write = do_sync_write,
        .aio_read = generic_file_aio_read,
        .aio_write = generic_file_aio_write,
+#endif
        .mmap = generic_file_mmap,
        .flush = yaffs_file_flush,
        .fsync = yaffs_sync_object,
        .splice_read = generic_file_splice_read,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+       .splice_write = iter_file_splice_write,
+#else
        .splice_write = generic_file_splice_write,
+#endif
        .llseek = generic_file_llseek,
 };
 
@@ -849,7 +909,7 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
 #endif
 
        if (error == 0)
-               error = inode_change_ok(inode, attr);
+               error = setattr_prepare(dentry, attr);
        if (error == 0) {
                int result;
                if (!error) {
@@ -883,10 +943,17 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
        return error;
 }
 
+#ifdef YAFFS_USE_XATTR
+#if (YAFFS_NEW_XATTR > 0)
+static int yaffs_setxattr(struct dentry *dentry, struct inode *inode,
+               const char *name, const void *value, size_t size, int flags)
+{
+#else
 static int yaffs_setxattr(struct dentry *dentry, const char *name,
                   const void *value, size_t size, int flags)
 {
        struct inode *inode = dentry->d_inode;
+#endif
        int error = 0;
        struct yaffs_dev *dev;
        struct yaffs_obj *obj = yaffs_inode_to_obj(inode);
@@ -910,10 +977,16 @@ static int yaffs_setxattr(struct dentry *dentry, const char *name,
        return error;
 }
 
+#if (YAFFS_NEW_XATTR > 0)
+static ssize_t yaffs_getxattr(struct dentry * dentry, struct inode *inode,
+       const char *name, void *buff, size_t size)
+{
+#else
 static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name,
                        void *buff, size_t size)
 {
        struct inode *inode = dentry->d_inode;
+#endif
        int error = 0;
        struct yaffs_dev *dev;
        struct yaffs_obj *obj = yaffs_inode_to_obj(inode);
@@ -961,6 +1034,7 @@ static int yaffs_removexattr(struct dentry *dentry, const char *name)
 
        return error;
 }
+#endif
 
 static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size)
 {
@@ -988,10 +1062,12 @@ static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size)
 
 static const struct inode_operations yaffs_file_inode_operations = {
        .setattr = yaffs_setattr,
+#ifdef YAFFS_USE_XATTR
        .setxattr = yaffs_setxattr,
        .getxattr = yaffs_getxattr,
-       .listxattr = yaffs_listxattr,
        .removexattr = yaffs_removexattr,
+#endif
+       .listxattr = yaffs_listxattr,
 };
 
 
@@ -1012,11 +1088,16 @@ static int yaffs_readlink(struct dentry *dentry, char __user * buffer,
        if (!alias)
                return -ENOMEM;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
        ret = vfs_readlink(dentry, buffer, buflen, alias);
+#else
+       ret = readlink_copy(buffer, buflen, alias);
+#endif
        kfree(alias);
        return ret;
 }
 
+#if (YAFFS_NEW_GET_LINK == 0)
 #if (YAFFS_NEW_FOLLOW_LINK == 1)
 static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
@@ -1055,7 +1136,28 @@ out:
        return ret;
 #endif
 }
+#else
+static const char *yaffs_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done)
+{
+       unsigned char *alias;
+       struct yaffs_dev *dev;
+
+       if (!dentry)
+               return ERR_PTR(-ECHILD);
+
+       dev = yaffs_dentry_to_obj(dentry)->my_dev;
 
+       yaffs_gross_lock(dev);
+
+       alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry));
+       yaffs_gross_unlock(dev);
+
+       if (!alias)
+               return ERR_PTR(-ENOMEM);
+       set_delayed_call(done, kfree_link, alias);
+       return alias;
+}
+#endif
 
 #ifdef YAFFS_HAS_PUT_INODE
 
@@ -1080,15 +1182,21 @@ void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias)
 
 static const struct inode_operations yaffs_symlink_inode_operations = {
        .readlink = yaffs_readlink,
+#if (YAFFS_NEW_GET_LINK == 1)
+       .get_link = yaffs_get_link,
+#else
        .follow_link = yaffs_follow_link,
+#endif
 #if (YAFFS_NEW_FOLLOW_LINK == 1)
        .put_link = yaffs_put_link,
 #endif
        .setattr = yaffs_setattr,
+#ifdef YAFFS_USE_XATTR
        .setxattr = yaffs_setxattr,
        .getxattr = yaffs_getxattr,
-       .listxattr = yaffs_listxattr,
        .removexattr = yaffs_removexattr,
+#endif
+       .listxattr = yaffs_listxattr,
 };
 
 #ifdef YAFFS_USE_OWN_IGET
@@ -1195,6 +1303,23 @@ struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,
 #define YCRED(x) (x->cred)
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+#define YPROC_uid(p) (YCRED(p)->fsuid)
+#define YPROC_gid(p) (YCRED(p)->fsgid)
+#define EXTRACT_gid(x) x
+#define EXTRACT_uid(x) x
+#define MAKE_gid(x) x
+#define MAKE_uid(x) x
+#else
+#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid)
+#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid)
+#define EXTRACT_gid(x) from_kgid(&init_user_ns, x)
+#define EXTRACT_uid(x) from_kuid(&init_user_ns, x)
+#define MAKE_gid(x) make_kgid(&init_user_ns, x)
+#define MAKE_uid(x) make_kuid(&init_user_ns, x)
+#endif
+
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
 static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
                       dev_t rdev)
@@ -1214,9 +1339,9 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
        struct yaffs_obj *parent = yaffs_inode_to_obj(dir);
 
        int error = -ENOSPC;
-       uid_t uid = YCRED(current)->fsuid;
+       uid_t uid = YPROC_uid(current);
        gid_t gid =
-           (dir->i_mode & S_ISGID) ? dir->i_gid : YCRED(current)->fsgid;
+           (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current);
 
        if ((dir->i_mode & S_ISGID) && S_ISDIR(mode))
                mode |= S_ISGID;
@@ -1302,7 +1427,10 @@ static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 }
 
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+                       bool dummy)
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
 static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                        struct nameidata *n)
 #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
@@ -1316,8 +1444,10 @@ static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
        return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
 }
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
-
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry,
+                                  unsigned int dummy)
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0))
 static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry,
                                   struct nameidata *n)
 #else
@@ -1408,9 +1538,9 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry,
 {
        struct yaffs_obj *obj;
        struct yaffs_dev *dev;
-       uid_t uid = YCRED(current)->fsuid;
+       uid_t uid = YPROC_uid(current);
        gid_t gid =
-           (dir->i_mode & S_ISGID) ? dir->i_gid : YCRED(current)->fsgid;
+           (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current);
 
        yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink");
 
@@ -1448,8 +1578,13 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry,
  *
  * NB: POSIX says you can rename an object over an old object of the same name
  */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry, unsigned int unused)
+#else
 static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        struct inode *new_dir, struct dentry *new_dentry)
+#endif
 {
        struct yaffs_dev *dev;
        int ret_val = YAFFS_FAIL;
@@ -1537,10 +1672,12 @@ static const struct inode_operations yaffs_dir_inode_operations = {
        .mknod = yaffs_mknod,
        .rename = yaffs_rename,
        .setattr = yaffs_setattr,
+       .listxattr = yaffs_listxattr,
+#ifdef YAFFS_USE_XATTR
        .setxattr = yaffs_setxattr,
        .getxattr = yaffs_getxattr,
-       .listxattr = yaffs_listxattr,
        .removexattr = yaffs_removexattr,
+#endif
 };
 
 /*-----------------------------------------------------------------*/
@@ -1658,19 +1795,90 @@ static void yaffs_remove_obj_callback(struct yaffs_obj *obj)
 
 /*-----------------------------------------------------------------*/
 
+#ifdef YAFFS_USE_DIR_ITERATE
+static int yaffs_iterate(struct file *f, struct dir_context *dc)
+{
+       struct yaffs_obj *obj;
+       struct yaffs_dev *dev;
+       struct yaffs_search_context *sc;
+       unsigned long curoffs;
+       struct yaffs_obj *l;
+       int ret_val = 0;
+
+       char name[YAFFS_MAX_NAME_LENGTH + 1];
+
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
+       dev = obj->my_dev;
+
+       yaffs_gross_lock(dev);
+
+       yaffs_dev_to_lc(dev)->readdir_process = current;
+
+       sc = yaffs_new_search(obj);
+       if (!sc) {
+               ret_val = -ENOMEM;
+               goto out;
+       }
+
+       if (!dir_emit_dots(f, dc))
+               return 0;
+
+       curoffs = 1;
+
+       while (sc->next_return) {
+               curoffs++;
+               l = sc->next_return;
+               if (curoffs >= dc->pos) {
+                       int this_inode = yaffs_get_obj_inode(l);
+                       int this_type = yaffs_get_obj_type(l);
+
+                       yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1);
+                       yaffs_trace(YAFFS_TRACE_OS,
+                               "yaffs_readdir: %s inode %d",
+                               name, yaffs_get_obj_inode(l));
+
+                       yaffs_gross_unlock(dev);
+
+                       if (!dir_emit(dc,
+                                     name,
+                                     strlen(name),
+                                     this_inode,
+                                     this_type)) {
+                               yaffs_gross_lock(dev);
+                               goto out;
+                       }
+
+                       yaffs_gross_lock(dev);
+
+                       dc->pos++;
+                       f->f_pos++;
+               }
+               yaffs_search_advance(sc);
+       }
+
+out:
+       yaffs_search_end(sc);
+       yaffs_dev_to_lc(dev)->readdir_process = NULL;
+       yaffs_gross_unlock(dev);
+
+       return ret_val;
+}
+
+#else
+
 static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
 {
        struct yaffs_obj *obj;
        struct yaffs_dev *dev;
        struct yaffs_search_context *sc;
-       struct inode *inode = f->f_dentry->d_inode;
+       struct inode *inode = Y_GET_DENTRY(f)->d_inode;
        unsigned long offset, curoffs;
        struct yaffs_obj *l;
        int ret_val = 0;
 
        char name[YAFFS_MAX_NAME_LENGTH + 1];
 
-       obj = yaffs_dentry_to_obj(f->f_dentry);
+       obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));
        dev = obj->my_dev;
 
        yaffs_gross_lock(dev);
@@ -1765,9 +1973,15 @@ out:
        return ret_val;
 }
 
+#endif
+
 static const struct file_operations yaffs_dir_operations = {
        .read = generic_read_dir,
+#ifdef YAFFS_USE_DIR_ITERATE
+       .iterate = yaffs_iterate,
+#else
        .readdir = yaffs_readdir,
+#endif
        .fsync = yaffs_sync_object,
        .llseek = generic_file_llseek,
 };
@@ -1813,8 +2027,8 @@ static void yaffs_fill_inode_from_obj(struct inode *inode,
 
                inode->i_ino = obj->obj_id;
                inode->i_mode = obj->yst_mode;
-               inode->i_uid = obj->yst_uid;
-               inode->i_gid = obj->yst_gid;
+               inode->i_uid = MAKE_uid(obj->yst_uid);
+               inode->i_gid = MAKE_gid(obj->yst_gid);
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
                inode->i_blksize = inode->i_sb->s_blocksize;
 #endif
@@ -1840,7 +2054,7 @@ static void yaffs_fill_inode_from_obj(struct inode *inode,
 
                yaffs_trace(YAFFS_TRACE_OS,
                        "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d",
-                       inode->i_mode, inode->i_uid, inode->i_gid,
+                       inode->i_mode, obj->yst_uid, obj->yst_gid,
                        inode->i_size, atomic_read(&inode->i_count));
 
                switch (obj->yst_mode & S_IFMT) {
@@ -2064,7 +2278,7 @@ static void yaffs_flush_inodes(struct super_block *sb)
                        yaffs_trace(YAFFS_TRACE_OS,
                                "flushing obj %d",
                                obj->obj_id);
-                       yaffs_flush_file(obj, 1, 0);
+                       yaffs_flush_file(obj, 1, 0, 0);
                }
        }
 }
@@ -2077,7 +2291,7 @@ static void yaffs_flush_super(struct super_block *sb, int do_checkpoint)
 
        yaffs_flush_inodes(sb);
        yaffs_update_dirty_dirs(dev);
-       yaffs_flush_whole_cache(dev);
+       yaffs_flush_whole_cache(dev, 1);
        if (do_checkpoint)
                yaffs_checkpoint_save(dev);
 }
@@ -2419,6 +2633,7 @@ static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint)
 }
 
 
+#ifdef YAFFS_HAS_WRITE_SUPER
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
 static void yaffs_write_super(struct super_block *sb)
 #else
@@ -2437,6 +2652,7 @@ static int yaffs_write_super(struct super_block *sb)
        return 0;
 #endif
 }
+#endif
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
 static int yaffs_sync_fs(struct super_block *sb, int wait)
@@ -2454,7 +2670,45 @@ static int yaffs_sync_fs(struct super_block *sb)
        return 0;
 }
 
+/* the function only is used to change dev->read_only when this file system
+ * is remounted.
+ */
+static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+       int read_only = 0;
+       struct mtd_info *mtd;
+       struct yaffs_dev *dev = 0;
+
+       /* Get the device */
+       mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+       if (!mtd) {
+               yaffs_trace(YAFFS_TRACE_ALWAYS,
+                       "MTD device #%u doesn't appear to exist",
+                       MINOR(sb->s_dev));
+               return 1;
+       }
+
+       /* Check it's NAND */
+       if (mtd->type != MTD_NANDFLASH) {
+               yaffs_trace(YAFFS_TRACE_ALWAYS,
+                       "MTD device is not NAND it's type %d",
+                       mtd->type);
+               return 1;
+       }
+
+       read_only = ((*flags & MS_RDONLY) != 0);
+       if (!read_only && !(mtd->flags & MTD_WRITEABLE)) {
+               read_only = 1;
+               printk(KERN_INFO
+                       "yaffs: mtd is read only, setting superblock read only");
+               *flags |= MS_RDONLY;
+       }
 
+       dev = sb->s_fs_info;
+       dev->read_only = read_only;
+
+       return 0;
+}
 
 static const struct super_operations yaffs_super_ops = {
        .statfs = yaffs_statfs,
@@ -2473,7 +2727,10 @@ static const struct super_operations yaffs_super_ops = {
        .clear_inode = yaffs_clear_inode,
 #endif
        .sync_fs = yaffs_sync_fs,
+#ifdef YAFFS_HAS_WRITE_SUPER
        .write_super = yaffs_write_super,
+#endif
+       .remount_fs = yaffs_remount_fs,
 };
 
 struct yaffs_options {
@@ -2590,6 +2847,7 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
        struct yaffs_param *param;
 
        int read_only = 0;
+       int inband_tags = 0;
 
        struct yaffs_options options;
 
@@ -2649,7 +2907,7 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
 
        /* Get the device */
        mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
-       if (!mtd) {
+       if (IS_ERR(mtd)) {
                yaffs_trace(YAFFS_TRACE_ALWAYS,
                        "yaffs: MTD device %u either not valid or unavailable",
                        MINOR(sb->s_dev));
@@ -2668,7 +2926,11 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
                yaffs_version = 1;
        }
 
-       if(yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags) < 0)
+       if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) ||
+           options.inband_tags)
+               inband_tags = 1;
+
+       if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0)
                return NULL;
 
        /* OK, so if we got here, we have an MTD that's NAND and looks
@@ -2688,15 +2950,11 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
        context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL);
 
        if (!dev || !context) {
-               if (dev)
-                       kfree(dev);
-               if (context)
-                       kfree(context);
+               kfree(dev);
+               kfree(context);
                dev = NULL;
                context = NULL;
-       }
 
-       if (!dev) {
                /* Deep shit could not allocate device structure */
                yaffs_trace(YAFFS_TRACE_ALWAYS,
                        "yaffs_read_super: Failed trying to allocate struct yaffs_dev."
@@ -2729,7 +2987,7 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
 
        param->n_reserved_blocks = 5;
        param->n_caches = (options.no_cache) ? 0 : 10;
-       param->inband_tags = options.inband_tags;
+       param->inband_tags = inband_tags;
 
        param->enable_xattr = 1;
        if (options.lazy_loading_overridden)
@@ -2744,6 +3002,10 @@ static struct super_block *yaffs_internal_read_super(int yaffs_version,
        param->refresh_period = 500;
        param->disable_summary = options.disable_summary;
 
+
+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING
+       param->disable_bad_block_marking  = 1;
+#endif
        if (options.empty_lost_and_found_overridden)
                param->empty_lost_n_found = options.empty_lost_and_found;
 
@@ -2974,6 +3236,9 @@ static struct proc_dir_entry *my_proc_entry;
 static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev)
 {
        struct yaffs_param *param = &dev->param;
+       int bs[10];
+
+       yaffs_count_blocks_by_state(dev,bs);
 
        buf += sprintf(buf, "start_block.......... %d\n", param->start_block);
        buf += sprintf(buf, "end_block............ %d\n", param->end_block);
@@ -2987,6 +3252,8 @@ static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev)
                                param->empty_lost_n_found);
        buf += sprintf(buf, "disable_lazy_load.... %d\n",
                                param->disable_lazy_load);
+       buf += sprintf(buf, "disable_bad_block_mrk %d\n",
+                               param->disable_bad_block_marking);
        buf += sprintf(buf, "refresh_period....... %d\n",
                                param->refresh_period);
        buf += sprintf(buf, "n_caches............. %d\n", param->n_caches);
@@ -2995,6 +3262,11 @@ static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev)
        buf += sprintf(buf, "always_check_erased.. %d\n",
                                param->always_check_erased);
        buf += sprintf(buf, "\n");
+       buf += sprintf(buf, "block count by state\n");
+       buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n",
+                               bs[0], bs[1], bs[2], bs[3], bs[4]);
+       buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n",
+                               bs[5], bs[6], bs[7], bs[8], bs[9]);
 
        return buf;
 }
@@ -3068,9 +3340,7 @@ static int yaffs_proc_read(char *page,
        /* Print header first */
        if (step == 0)
                buf +=
-                   sprintf(buf,
-                           "Multi-version YAFFS built:" __DATE__ " " __TIME__
-                           "\n");
+                   sprintf(buf, "Multi-version YAFFS\n");
        else if (step == 1)
                buf += sprintf(buf, "\n");
        else {
@@ -3149,7 +3419,7 @@ static struct {
 
 #define MAX_MASK_NAME_LENGTH 40
 static int yaffs_proc_write_trace_options(struct file *file, const char *buf,
-                                         unsigned long count, void *data)
+                                         unsigned long count)
 {
        unsigned rg = 0, mask_bitfield;
        char *end;
@@ -3244,10 +3514,99 @@ static int yaffs_proc_write_trace_options(struct file *file, const char *buf,
        return count;
 }
 
+/* Debug strings are of the form:
+ * .bnnn         print info on block n
+ * .cobjn,chunkn print nand chunk id for objn:chunkn
+ */
+
+static int yaffs_proc_debug_write(struct file *file, const char *buf,
+                                         unsigned long count)
+{
+
+       char str[100];
+       char *p0;
+       char *p1;
+       long p1_val;
+       long p0_val;
+       char cmd;
+       struct list_head *item;
+
+       memset(str, 0, sizeof(str));
+       memcpy(str, buf, min((size_t)count, sizeof(str) -1));
+
+       cmd = str[1];
+
+       p0 = str + 2;
+
+       p1 = p0;
+
+       while (*p1 && *p1 != ',') {
+               p1++;
+       }
+       *p1 = '\0';
+       p1++;
+
+       p0_val = simple_strtol(p0, NULL, 0);
+       p1_val = simple_strtol(p1, NULL, 0);
+
+
+       mutex_lock(&yaffs_context_lock);
+
+       /* Locate and print the Nth entry.  Order N-squared but N is small. */
+       list_for_each(item, &yaffs_context_list) {
+               struct yaffs_linux_context *dc =
+                   list_entry(item, struct yaffs_linux_context,
+                              context_list);
+               struct yaffs_dev *dev = dc->dev;
+
+               if (cmd == 'b') {
+                       struct yaffs_block_info *bi;
+
+                       bi = yaffs_get_block_info(dev,p0_val);
+
+                       if(bi) {
+                               printk("Block %d: state %d, retire %d, use %d, seq %d\n",
+                                       (int)p0_val, bi->block_state,
+                                       bi->needs_retiring, bi->pages_in_use,
+                                       bi->seq_number);
+                       }
+               } else if (cmd == 'c') {
+                       struct yaffs_obj *obj;
+                       int nand_chunk;
+
+                       obj = yaffs_find_by_number(dev, p0_val);
+                       if (!obj)
+                               printk("No obj %d\n", (int)p0_val);
+                       else {
+                               if(p1_val == 0)
+                                       nand_chunk = obj->hdr_chunk;
+                               else
+                                       nand_chunk =
+                                               yaffs_find_chunk_in_file(obj,
+                                                       p1_val, NULL);
+                               printk("Nand chunk for %d:%d is %d\n",
+                                       (int)p0_val, (int)p1_val, nand_chunk);
+                       }
+               }
+       }
+
+       mutex_unlock(&yaffs_context_lock);
+
+       return count;
+}
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
 static int yaffs_proc_write(struct file *file, const char *buf,
-                           unsigned long count, void *data)
+                           unsigned long count, void *ppos)
+#else
+static ssize_t yaffs_proc_write(struct file *file, const char __user *buf,
+                           size_t count, loff_t *ppos)
+#endif
 {
-       return yaffs_proc_write_trace_options(file, buf, count, data);
+       if (buf[0] == '.')
+               return yaffs_proc_debug_write(file, buf, count);
+       return yaffs_proc_write_trace_options(file, buf, count);
 }
 
 /* Stuff to handle installation of file systems */
@@ -3262,16 +3621,52 @@ static struct file_system_to_install fs_to_install[] = {
        {NULL, 0}
 };
 
-static int __init init_yaffs_fs(void)
+
+#ifdef YAFFS_NEW_PROCFS
+static int yaffs_proc_show(struct seq_file *m, void *v)
 {
-       int error = 0;
-       struct file_system_to_install *fsinst;
+       /* FIXME: Unify in a better way? */
+       char buffer[512];
+       char *start;
+       int len;
 
-       yaffs_trace(YAFFS_TRACE_ALWAYS,
-               "yaffs built " __DATE__ " " __TIME__ " Installing.");
+       len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL);
+       seq_puts(m, buffer);
+       return 0;
+}
 
-       mutex_init(&yaffs_context_lock);
+static int yaffs_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, yaffs_proc_show, NULL);
+}
 
+static struct file_operations procfs_ops = {
+       .owner = THIS_MODULE,
+       .open  = yaffs_proc_open,
+       .read  = seq_read,
+       .write = yaffs_proc_write,
+};
+
+static int yaffs_procfs_init(void)
+{
+       /* Install the proc_fs entries */
+       my_proc_entry = proc_create("yaffs",
+                                   S_IRUGO | S_IFREG,
+                                   YPROC_ROOT,
+                                   &procfs_ops);
+
+       if (my_proc_entry) {
+               return 0;
+       } else {
+               return -ENOMEM;
+       }
+}
+
+#else
+
+
+static int yaffs_procfs_init(void)
+{
        /* Install the proc_fs entries */
        my_proc_entry = create_proc_entry("yaffs",
                                          S_IRUGO | S_IFREG, YPROC_ROOT);
@@ -3280,9 +3675,28 @@ static int __init init_yaffs_fs(void)
                my_proc_entry->write_proc = yaffs_proc_write;
                my_proc_entry->read_proc = yaffs_proc_read;
                my_proc_entry->data = NULL;
+               return 0;
        } else {
                return -ENOMEM;
-        }
+       }
+}
+
+#endif
+
+
+static int __init init_yaffs_fs(void)
+{
+       int error = 0;
+       struct file_system_to_install *fsinst;
+
+       yaffs_trace(YAFFS_TRACE_ALWAYS,
+               "yaffs Installing.");
+
+       mutex_init(&yaffs_context_lock);
+
+       error = yaffs_procfs_init();
+       if (error)
+               return error;
 
        /* Now add the file system entries */
 
@@ -3317,7 +3731,7 @@ static void __exit exit_yaffs_fs(void)
        struct file_system_to_install *fsinst;
 
        yaffs_trace(YAFFS_TRACE_ALWAYS,
-               "yaffs built " __DATE__ " " __TIME__ " removing.");
+               "yaffs removing.");
 
        remove_proc_entry("yaffs", YPROC_ROOT);