diff --git a/lustre/include/linux/lustre_fsfilt.h b/lustre/include/linux/lustre_fsfilt.h
index 988f8e62aae208c9d07ceeaeeaa7a6dff23a154f..978fbe4a4974b0fb9bff4e64dbd11d7062a3ca61 100644
--- a/lustre/include/linux/lustre_fsfilt.h
+++ b/lustre/include/linux/lustre_fsfilt.h
@@ -109,8 +109,10 @@ struct fsfilt_operations {
         int     (* fs_init_extents_ea)(struct inode *inode); 
         int     (* fs_insert_extents_ea)(struct inode *inode, unsigned long from, 
                                          unsigned long num); 
+        int     (* fs_do_write_cow)(struct dentry *dentry, void *extents, int nexts);
         int     (* fs_write_extents)(struct dentry *dentry, 
                                      unsigned long offset, unsigned long blks);
+        int     (* fs_get_fs_flags)(struct dentry *dentry);
         int     (* fs_remove_extents_ea)(struct inode *inode, unsigned long from, 
                                          unsigned long num); 
         int     (* fs_get_ino_write_extents)(struct super_block *sb, ino_t ino, 
@@ -493,6 +495,13 @@ static inline int fsfilt_map_inode_pages(struct obd_device *obd,
         return obd->obd_fsops->fs_map_inode_pages(inode, page, pages, blocks,
                                                   created, create, sem);
 }
+static inline int fsfilt_get_fs_flags(struct obd_device *obd, 
+                                      struct dentry *dentry)
+{
+        if (obd->obd_fsops->fs_get_fs_flags) 
+                return obd->obd_fsops->fs_get_fs_flags(dentry);
+        return 0;
+}
 
 static inline int 
 fsfilt_write_extents(struct obd_device *obd, struct dentry *dentry, 
@@ -503,7 +512,15 @@ fsfilt_write_extents(struct obd_device *obd, struct dentry *dentry,
                                                         offset, blks);
         return 0;
 }
-
+static inline int
+fsfilt_do_write_cow(struct obd_device *obd, struct dentry *dentry,
+                    void *extents, int num_extents)
+{
+        if (obd->obd_fsops->fs_do_write_cow)
+                return obd->obd_fsops->fs_do_write_cow(dentry, extents,
+                                                       num_extents);
+        return 0;
+}
 static inline int
 fs_prep_san_write(struct obd_device *obd, struct inode *inode,
                   long *blocks, int nblocks, loff_t newsize)
diff --git a/lustre/include/linux/lustre_smfs.h b/lustre/include/linux/lustre_smfs.h
index 5bdea663fbf69e3df4185f28e2c8946046d1d6d7..7bb62cb4bdae7778e2997e3488b7016b017f1c72 100644
--- a/lustre/include/linux/lustre_smfs.h
+++ b/lustre/include/linux/lustre_smfs.h
@@ -30,7 +30,6 @@ struct snap_inode_info {
 	int sn_flags;		/* the flags indicated inode type */
 	int sn_gen; 	        /*the inode generation*/
 };
-
 struct smfs_inode_info {
         struct inode *smi_inode;
         __u32  smi_flags;
@@ -463,7 +462,6 @@ extern int smfs_rec_md(struct inode *inode, void * lmm, int lmm_size);
 extern int smfs_rec_unpack(struct smfs_proc_args *args, char *record,
                            char **pbuf, int *opcode);
 	
-int smfs_cow(struct inode *dir, struct dentry *dentry, int op);
 
 extern int smfs_post_setup(struct super_block *sb, struct vfsmount *mnt);
 extern int smfs_post_cleanup(struct super_block *sb);
diff --git a/lustre/include/linux/lustre_snap.h b/lustre/include/linux/lustre_snap.h
index 38531f683538d36cf5e3898e42c76a2a1d93b768..cd8e9c64f445c07c1d070cce1017cbea5a22d5b5 100644
--- a/lustre/include/linux/lustre_snap.h
+++ b/lustre/include/linux/lustre_snap.h
@@ -145,6 +145,7 @@ struct snap_ea{
 #define SNAPTABLE_MAGIC	        0x19760218
 #define SNAPTABLE_INFO          "snaptable"
 #define SNAP_GENERATION         "snap_generation"
+
 struct snap {
         time_t          sn_time;
         unsigned int    sn_index;
@@ -172,4 +173,13 @@ struct snap_info {
 extern int smfs_add_snap_item(struct super_block *sb, char *name);
 extern int smfs_start_cow(struct super_block *sb);
 extern int smfs_stop_cow(struct super_block *sb);
+
+struct write_extents {
+       size_t w_count;
+       loff_t w_pos; 
+};
+int smfs_cow(struct inode *dir, struct dentry *dentry,
+             void *data1, void *data2, int op);
+int smfs_cow_write(struct inode *inode, struct dentry *dentry, void *data1,
+                   void *data2);
 #endif /*_LUSTRE_SNAP_H*/
diff --git a/lustre/include/linux/obd.h b/lustre/include/linux/obd.h
index 75bd3d44a3a21de9c8bf968dda85e8d51aafb15a..3f1bd99843c8dbff78c7dbb5301d0948369cb8f3 100644
--- a/lustre/include/linux/obd.h
+++ b/lustre/include/linux/obd.h
@@ -709,8 +709,11 @@ struct obd_ops {
                           int objcount, struct obd_ioobj *obj,
                           int niocount, struct niobuf_local *local,
                           struct obd_trans_info *oti, int rc);
+        int (*o_do_cow)(struct obd_export *exp, struct obd_ioobj *obj, 
+                        int objcount, struct niobuf_remote *rnb);
         int (*o_write_extents)(struct obd_export *exp, struct obd_ioobj *obj,
-                               int niocount, struct niobuf_local *local,int rc);
+                               int objcount, int niocount, 
+                               struct niobuf_local *local,int rc);
         int (*o_enqueue)(struct obd_export *, struct lov_stripe_md *,
                          __u32 type, ldlm_policy_data_t *, __u32 mode,
                          int *flags, void *bl_cb, void *cp_cb, void *gl_cb,
diff --git a/lustre/include/linux/obd_class.h b/lustre/include/linux/obd_class.h
index 4af2936c9f00b8ac9ef55dcd20a65d2911bdb0f6..85edc8c23b30d1047c84cc9ce023e18c2e94b702 100644
--- a/lustre/include/linux/obd_class.h
+++ b/lustre/include/linux/obd_class.h
@@ -902,8 +902,28 @@ static inline int obd_commitrw(int cmd, struct obd_export *exp, struct obdo *oa,
         RETURN(rc);
 }
 
-static inline int obd_write_extents(struct obd_export *exp, struct obd_ioobj *obj,
-                                    int niocount, struct niobuf_local *local, int rc)
+static inline int obd_do_cow(struct obd_export *exp, struct obd_ioobj *obj,
+                            int objcount,struct niobuf_remote *rnb)
+{
+        int rc;
+        ENTRY;
+
+        /* there are cases when write_extents is not implemented. */
+        if (!OBP(exp->exp_obd, do_cow))
+                RETURN(0);
+                
+        OBD_COUNTER_INCREMENT(exp->exp_obd, do_cow);
+
+        rc = OBP(exp->exp_obd, do_cow)(exp, obj, objcount, rnb);
+
+        RETURN(rc);
+}
+
+static inline int obd_write_extents(struct obd_export *exp, 
+                                    struct obd_ioobj *obj,
+                                    int objcount, int niocount,  
+                                    struct niobuf_local *local, 
+                                    int rc)
 {
         ENTRY;
 
@@ -913,7 +933,8 @@ static inline int obd_write_extents(struct obd_export *exp, struct obd_ioobj *ob
                 
         OBD_COUNTER_INCREMENT(exp->exp_obd, write_extents);
 
-        rc = OBP(exp->exp_obd, write_extents)(exp, obj, niocount, local, rc);
+        rc = OBP(exp->exp_obd, write_extents)(exp, obj, objcount, niocount, 
+                                              local, rc);
         RETURN(rc);
 }
 
diff --git a/lustre/lvfs/fsfilt_smfs.c b/lustre/lvfs/fsfilt_smfs.c
index eefd536d0750b3c64d722d26fe59008ad5405bab..2624159f77a52cdb8270af47f97b55eb832ee0d9 100644
--- a/lustre/lvfs/fsfilt_smfs.c
+++ b/lustre/lvfs/fsfilt_smfs.c
@@ -592,7 +592,7 @@ static int fsfilt_smfs_post_setup(struct obd_device *obd, struct vfsmount *mnt)
                 smfs_post_setup(sb, mnt);
                 if (SMFS_DO_REC(S2SMI(sb)))
                         rc = smfs_start_rec(sb, mnt);
-#ifdef CONFIG_SNAPFS
+#if CONFIG_SNAPFS
                 if (SMFS_DO_COW(S2SMI(sb)))
                         rc = smfs_start_cow(sb);
 #endif
@@ -614,12 +614,13 @@ static int fsfilt_smfs_post_cleanup(struct obd_device *obd,
 {
         struct super_block *sb = NULL;
         int rc = 0;
-
+        ENTRY;
+        
         if (mnt) {
                 sb = mnt->mnt_sb;
                 if (SMFS_DO_REC(S2SMI(sb)))
                         rc = smfs_stop_rec(sb);
-#ifdef CONFIG_SNAPFS
+#if CONFIG_SNAPFS
                 if (SMFS_DO_COW(S2SMI(sb)))
                         rc = smfs_stop_cow(sb);
 #endif
@@ -631,6 +632,8 @@ static int fsfilt_smfs_post_cleanup(struct obd_device *obd,
 static int fsfilt_smfs_set_fs_flags(struct inode *inode, int flags)
 {
         int rc = 0;
+        ENTRY;
+
         if (SMFS_DO_REC(S2SMI(inode->i_sb)) && (flags & SM_DO_REC))
                 SMFS_SET_INODE_REC(inode);
         if (SMFS_DO_COW(S2SMI(inode->i_sb)) && (flags & SM_DO_COW))
@@ -641,6 +644,8 @@ static int fsfilt_smfs_set_fs_flags(struct inode *inode, int flags)
 static int fsfilt_smfs_clear_fs_flags(struct inode *inode, int flags)
 {
         int rc = 0;
+        ENTRY;
+        
         if (SMFS_DO_REC(S2SMI(inode->i_sb)) && (flags & SM_DO_REC))
                 SMFS_CLEAN_INODE_REC(inode);
         if (SMFS_DO_COW(S2SMI(inode->i_sb)) && (flags & SM_DO_COW))
@@ -648,6 +653,21 @@ static int fsfilt_smfs_clear_fs_flags(struct inode *inode, int flags)
         RETURN(rc);
 }
 
+static int fsfilt_smfs_get_fs_flags(struct dentry *de)
+{
+        struct inode *inode = de->d_inode;
+        int flags = 0;
+        ENTRY;
+
+        LASSERT(inode);
+
+        if (SMFS_DO_REC(S2SMI(inode->i_sb)) && SMFS_DO_INODE_REC(inode))
+                flags |= SM_DO_REC;
+        if (SMFS_DO_COW(S2SMI(inode->i_sb)) && SMFS_DO_INODE_COW(inode))
+                flags |= SM_DO_COW;
+       
+        RETURN(flags); 
+}
 static int fsfilt_smfs_set_ost_flags(struct super_block *sb)
 {
         int rc = 0;
@@ -928,7 +948,25 @@ static int fsfilt_smfs_set_snap_item(struct super_block *sb, char *name)
 #endif
         RETURN(rc);        
 }
-
+static int fsfilt_smfs_do_write_cow(struct dentry *de, void *extents,
+                                    int num_extents)
+{
+        int rc = 0;
+#if CONFIG_SNAPFS
+        struct write_extents *w_ext = (struct write_extents *)extents;
+        int i = 0;
+        ENTRY;
+        for (i = 0; i < num_extents; i++) {
+               size_t count = w_ext->w_count;
+               loff_t off = w_ext->w_pos;
+               rc = smfs_cow_write(de->d_inode, de, &count, &off);
+               if (rc)
+                        RETURN(rc);  
+               w_ext ++;
+        }
+#endif
+        RETURN(rc);
+}
 static struct fsfilt_operations fsfilt_smfs_ops = {
         .fs_type                = "smfs",
         .fs_owner               = THIS_MODULE,
@@ -955,6 +993,7 @@ static struct fsfilt_operations fsfilt_smfs_ops = {
         .fs_post_cleanup        = fsfilt_smfs_post_cleanup,
         .fs_set_fs_flags       = fsfilt_smfs_set_fs_flags,
         .fs_clear_fs_flags     = fsfilt_smfs_clear_fs_flags,
+        .fs_get_fs_flags       = fsfilt_smfs_get_fs_flags,
         .fs_set_ost_flags       = fsfilt_smfs_set_ost_flags,
         .fs_set_mds_flags       = fsfilt_smfs_set_mds_flags,
         .fs_precreate_rec       = fsfilt_smfs_precreate_rec,
@@ -969,7 +1008,7 @@ static struct fsfilt_operations fsfilt_smfs_ops = {
         .fs_free_write_extents  = fsfilt_smfs_free_extents,
         .fs_write_extents       = fsfilt_smfs_write_extents,
         .fs_set_snap_item       = fsfilt_smfs_set_snap_item,
-        
+        .fs_do_write_cow        = fsfilt_smfs_do_write_cow,
         /* FIXME-UMKA: probably fsfilt_smfs_get_op_len() should be
          * put here too. */
 };
diff --git a/lustre/lvfs/fsfilt_snap_ext3.c b/lustre/lvfs/fsfilt_snap_ext3.c
index 885bae732869a395160b1c0d815e1b75f1ad4626..c538dc486b679cbf52434fd9ef02120d585eb1cd 100644
--- a/lustre/lvfs/fsfilt_snap_ext3.c
+++ b/lustre/lvfs/fsfilt_snap_ext3.c
@@ -324,42 +324,74 @@ static void ext3_copy_meta(handle_t *handle, struct inode *dst, struct inode *sr
 		}
 	}
 }
-/* fsfilt_ext3_copy_block - copy one data block from inode @src to @dst.
-   No lock here.  User should do the lock.
-   User should check the return value to see if the result is correct.
-   Return value:
-   1:    The block has been copied successfully
-   0:    No block is copied, usually this is because src has no such blk
-  -1:    Error
-*/
+static int ext3_copy_reg_block(struct inode *dst, struct inode *src, int blk)
+{
+        struct page     *src_page, *dst_page; 
+        loff_t          offset = blk << src->i_sb->s_blocksize_bits;
+        unsigned long   index = offset >> PAGE_CACHE_SHIFT;
+        int             rc = 0;
+        ENTRY;
+        
+        /*read the src page*/
+        src_page = grab_cache_page(src->i_mapping, index);
+        if (src_page == NULL)
+                RETURN(-ENOMEM);
+
+        if (!PageUptodate(src_page)) {
+                rc = src->i_mapping->a_ops->readpage(NULL, src_page);
+                if (rc < 0) {
+                        page_cache_release(src_page);
+                        RETURN(rc);
+                }
+        }
+        kmap(src_page);
+        /*get dst page*/
+        
+        dst_page = grab_cache_page(dst->i_mapping, index);
+        if (dst_page == NULL)
+                GOTO(src_page_unlock, rc = -ENOMEM);
+        kmap(dst_page);
+        
+        /*FIXME: should use mapping ops or block_prepare_write to prepare the block*/ 
+        rc = block_prepare_write(dst_page, 0, PAGE_CACHE_SIZE, ext3_get_block);
+        if (rc) {
+                CERROR("inode %lu, prepare write rc=%d \n", dst->i_ino, rc);
+                GOTO(dst_page_unlock, rc);
+        }
                                                                                                                                                                                                      
-static int fsfilt_ext3_copy_block (struct inode *dst, struct inode *src, int blk)
+        memcpy(page_address(dst_page), page_address(src_page), PAGE_CACHE_SIZE);
+                                                                                                                                                                                                     
+        generic_commit_write(NULL, dst_page, 0, PAGE_CACHE_SIZE);
+
+dst_page_unlock:
+       kunmap(dst_page);
+       UnlockPage(dst_page);
+       page_cache_release(dst_page);
+src_page_unlock:
+       kunmap(src_page);
+       page_cache_release(src_page);
+
+       RETURN(rc);
+}
+static int ext3_copy_dir_block(struct inode *dst, struct inode *src, int blk)
 {
         struct buffer_head *bh_dst = NULL, *bh_src = NULL;
-        int err = 0;
+        int rc = 0;
         handle_t *handle = NULL;
         ENTRY;                                                                                                                                                                                             
-        CDEBUG(D_INODE, "copy blk %d from %lu to %lu \n", blk, src->i_ino, 
-               dst->i_ino);
-        /*
-         * ext3_getblk() require handle!=NULL
-         */
-        if (S_ISREG(src->i_mode)) 
-                RETURN(0);
-
         handle = ext3_journal_start(dst, SNAP_COPYBLOCK_TRANS_BLOCKS);
         if( !handle )
                 RETURN(-EINVAL);
                                                                                                                                                                                                      
-        bh_src = ext3_bread(handle, src, blk, 0, &err);
+        bh_src = ext3_bread(handle, src, blk, 0, &rc);
         if (!bh_src) {
-                CERROR("error for src blk %d, error %d\n", blk, err);
-                GOTO(exit_relese, err);
+                CERROR("rcor for src blk %d, rcor %d\n", blk, rc);
+                GOTO(exit_relese, rc);
         }
-        bh_dst = ext3_getblk(handle, dst, blk, 1, &err);
+        bh_dst = ext3_getblk(handle, dst, blk, 1, &rc);
         if (!bh_dst) {
-                CERROR("error for dst blk %d, error %d\n", blk, err);
-                GOTO(exit_relese, err);
+                CERROR("rcor for dst blk %d, rcor %d\n", blk, rc);
+                GOTO(exit_relese, rc);
         }
         CDEBUG(D_INODE, "copy block %lu to %lu (%ld bytes)\n",
                bh_src->b_blocknr, bh_dst->b_blocknr, src->i_sb->s_blocksize);
@@ -367,14 +399,40 @@ static int fsfilt_ext3_copy_block (struct inode *dst, struct inode *src, int blk
         ext3_journal_get_write_access(handle, bh_dst);
         memcpy(bh_dst->b_data, bh_src->b_data, src->i_sb->s_blocksize);
         ext3_journal_dirty_metadata(handle, bh_dst);
-        err = 1;
+        rc = 1;
 
 exit_relese:
         if (bh_src) brelse(bh_src);
         if (bh_dst) brelse(bh_dst);
         if (handle)
                 ext3_journal_stop(handle, dst);
-        RETURN(err);
+        RETURN(rc);
+}
+/* fsfilt_ext3_copy_block - copy one data block from inode @src to @dst.
+   No lock here.  User should do the lock.
+   User should check the return value to see if the result is correct.
+   Return value:
+   1:    The block has been copied successfully
+   0:    No block is copied, usually this is because src has no such blk
+  -1:    Error
+*/
+                                                                                                                                                                                                     
+static int fsfilt_ext3_copy_block (struct inode *dst, struct inode *src, int blk)
+{
+        int rc = 0;
+        ENTRY;                                                                                                                                                                                             
+        CDEBUG(D_INODE, "copy blk %d from %lu to %lu \n", blk, src->i_ino, 
+               dst->i_ino);
+        /*
+         * ext3_getblk() require handle!=NULL
+         */
+        if (S_ISREG(src->i_mode)) { 
+                rc = ext3_copy_reg_block(dst, src, blk);
+        } else {
+                rc = ext3_copy_dir_block(dst, src, blk);
+        }
+
+        RETURN(rc);
 }
                                                                                                                                                                                              
 static inline int ext3_has_ea(struct inode *inode)
diff --git a/lustre/obdclass/lprocfs_status.c b/lustre/obdclass/lprocfs_status.c
index 7879648665998b53f110ef2f52d48d51df31b4b2..6614c53008f8e1dc5b1848e00948c5fcaf12cd1d 100644
--- a/lustre/obdclass/lprocfs_status.c
+++ b/lustre/obdclass/lprocfs_status.c
@@ -640,6 +640,7 @@ int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iterate);
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
+        LPROCFS_OBD_OP_INIT(num_private_stats, stats, do_cow);
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, write_extents);
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, enqueue);
         LPROCFS_OBD_OP_INIT(num_private_stats, stats, match);
diff --git a/lustre/obdfilter/filter.c b/lustre/obdfilter/filter.c
index 81bcd9ba5c017b6468ef29b5d9398276d3a67abe..7adb22d5897c1c2ae063141cca2358c607c669c9 100644
--- a/lustre/obdfilter/filter.c
+++ b/lustre/obdfilter/filter.c
@@ -2136,7 +2136,7 @@ static int filter_precreate(struct obd_device *obd, struct obdo *oa,
 
                 /*only do precreate rec record. so clean kml flags here*/
                 fsfilt_clear_fs_flags(obd, dparent->d_inode, 
-                                      SM_DO_REC | SM_DO_COW);
+                                      SM_DO_REC);
                 
                 dchild = filter_fid2dentry(obd, dparent, group, next_id);
                 if (IS_ERR(dchild))
@@ -2183,7 +2183,7 @@ static int filter_precreate(struct obd_device *obd, struct obdo *oa,
                                 CERROR("unable to write lastobjid "
                                        "but file created\n");
                 }
-                fsfilt_set_fs_flags(obd, dparent->d_inode, SM_DO_REC | SM_DO_COW);
+                fsfilt_set_fs_flags(obd, dparent->d_inode, SM_DO_REC);
         
         cleanup:
                 switch(cleanup_phase) {
@@ -2736,6 +2736,13 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp,
                 RETURN(rc);
         }
 
+        case OBD_IOC_SNAP_ADD: {
+                char *name = data->ioc_inlbuf1;
+                if (name) {
+                        rc = fsfilt_set_snap_item(obd, obd->u.filter.fo_sb, name);
+                }
+                RETURN(rc);
+        }
         case OBD_IOC_LLOG_CANCEL:
         case OBD_IOC_LLOG_REMOVE:
         case OBD_IOC_LLOG_INFO:
@@ -2863,6 +2870,7 @@ static struct obd_ops filter_obd_ops = {
         .o_sync           = filter_sync,
         .o_preprw         = filter_preprw,
         .o_commitrw       = filter_commitrw,
+        .o_do_cow         = filter_do_cow,
         .o_write_extents  = filter_write_extents,
         .o_destroy_export = filter_destroy_export,
         .o_llog_init      = filter_llog_init,
@@ -2893,6 +2901,7 @@ static struct obd_ops filter_sanobd_ops = {
         .o_sync           = filter_sync,
         .o_preprw         = filter_preprw,
         .o_commitrw       = filter_commitrw,
+        .o_do_cow         = filter_do_cow,
         .o_write_extents  = filter_write_extents,
         .o_san_preprw     = filter_san_preprw,
         .o_destroy_export = filter_destroy_export,
diff --git a/lustre/obdfilter/filter_internal.h b/lustre/obdfilter/filter_internal.h
index e974b3defd86683e1e5c23b2d828f0b3012abe54..0ad0e3eb536f5bec80e7f0cd69678f58593cea9f 100644
--- a/lustre/obdfilter/filter_internal.h
+++ b/lustre/obdfilter/filter_internal.h
@@ -118,8 +118,10 @@ int filter_commitrw(int cmd, struct obd_export *, struct obdo *, int objcount,
                     struct obd_ioobj *, int niocount, struct niobuf_local *,
                     struct obd_trans_info *, int rc);
 int filter_write_extents(struct obd_export *exp, struct obd_ioobj *obj,
-                         int niocount, struct niobuf_local *local, 
+                         int objcount, int niocount,struct niobuf_local *local, 
                          int rc);
+int filter_do_cow(struct obd_export *exp, struct obd_ioobj *obj,
+                  int nioo, struct niobuf_remote *rnb);
 int filter_brw(int cmd, struct obd_export *, struct obdo *,
 	       struct lov_stripe_md *, obd_count oa_bufs, struct brw_page *,
 	       struct obd_trans_info *);
diff --git a/lustre/obdfilter/filter_io.c b/lustre/obdfilter/filter_io.c
index 0eac47f0df1392137e140eea36674c9189d9a2a3..10cc687fe04f1255723727cadd5550c38064d55a 100644
--- a/lustre/obdfilter/filter_io.c
+++ b/lustre/obdfilter/filter_io.c
@@ -33,6 +33,8 @@
 
 #include <linux/obd_class.h>
 #include <linux/lustre_fsfilt.h>
+#include <linux/lustre_smfs.h>
+#include <linux/lustre_snap.h>
 #include "filter_internal.h"
 
 static int filter_start_page_read(struct obd_device *obd, struct inode *inode,
@@ -832,22 +834,81 @@ void filter_grant_commit(struct obd_export *exp, int niocount,
 
         spin_unlock(&exp->exp_obd->obd_osfs_lock);
 }
+int filter_do_cow(struct obd_export *exp, struct obd_ioobj *obj,
+                  int nioo, struct niobuf_remote *rnb)
+{
+        struct dentry *dentry;
+        struct lvfs_run_ctxt saved;
+        struct write_extents *extents = NULL;
+        int j, rc = 0, numexts = 0, flags = 0;
+
+        ENTRY;
+
+        LASSERT(nioo == 1);
+
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
+        
+        dentry = filter_fid2dentry(exp->exp_obd, NULL, obj->ioo_gr,
+                                   obj->ioo_id);
+        if (IS_ERR(dentry)) {
+                pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
+                RETURN (PTR_ERR(dentry));
+        }
 
-int filter_write_extents(struct obd_export *exp, struct obd_ioobj *obj,
-                         int niocount, struct niobuf_local *local,
-                         int rc)
+        if (dentry->d_inode == NULL) {
+                CERROR("trying to write extents to non-existent file "LPU64"\n",
+                       obj->ioo_id);
+                GOTO(cleanup, rc = -ENOENT);
+        }
+        
+        flags = fsfilt_get_fs_flags(exp->exp_obd, dentry);
+        if (!(flags & SM_DO_COW)) {
+                GOTO(cleanup, rc);
+        }
+        OBD_ALLOC(extents, obj->ioo_bufcnt * sizeof(struct write_extents)); 
+        if (!extents) {
+                CERROR("No Memory\n");
+                GOTO(cleanup, rc = -ENOMEM);
+        }
+        for (j = 0; j < obj->ioo_bufcnt; j++) {
+                if (rnb[j].len != 0) {
+                        extents[numexts].w_count = rnb[j].len;
+                        extents[numexts].w_pos = rnb[j].offset;
+                        numexts++;
+                } 
+        } 
+        rc = fsfilt_do_write_cow(exp->exp_obd, dentry, extents, numexts);
+        if (rc) {
+                CERROR("Do cow error id "LPU64" rc:%d \n",
+                        obj->ioo_id, rc);
+                GOTO(cleanup, rc); 
+        }
+        
+cleanup:
+        if (extents) {
+                OBD_FREE(extents, obj->ioo_bufcnt * sizeof(struct write_extents));
+        }
+        f_dput(dentry);
+        pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
+        RETURN(rc);
+
+}
+int filter_write_extents(struct obd_export *exp, struct obd_ioobj *obj, int nobj,
+                         int niocount, struct niobuf_local *local, int rc)
 {
         struct lvfs_run_ctxt saved;
         struct dentry *dentry;
         struct niobuf_local *lnb;
         __u64  offset = 0;
         __u32  len = 0;
-        int    i; 
+        int    i, flags; 
  
         ENTRY;
 
+        LASSERT(nobj == 1);
+
         push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
-        
+
         dentry = filter_fid2dentry(exp->exp_obd, NULL, obj->ioo_gr,
                                    obj->ioo_id);
         if (IS_ERR(dentry)) {
@@ -861,6 +922,11 @@ int filter_write_extents(struct obd_export *exp, struct obd_ioobj *obj,
                 GOTO(cleanup, rc = -ENOENT);
         }
         
+        flags = fsfilt_get_fs_flags(exp->exp_obd, dentry);
+        if (!(flags & SM_DO_REC)) {
+                GOTO(cleanup, rc);
+        }
+
         for (i = 0, lnb = local; i < obj->ioo_bufcnt; i++, lnb++) {
                 if (len == 0) {
                         offset = lnb->offset;
@@ -891,7 +957,7 @@ int filter_write_extents(struct obd_export *exp, struct obd_ioobj *obj,
 cleanup:
         f_dput(dentry);
         pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
-        return rc;
+        RETURN(rc);
 }
 
 int filter_commitrw(int cmd, struct obd_export *exp, struct obdo *oa,
diff --git a/lustre/ost/ost_handler.c b/lustre/ost/ost_handler.c
index bac04c274c4afbb9143d291952f09906df2741ab..3cbf75f548f4fb433490ffe6fdd95c66a75ad4ec 100644
--- a/lustre/ost/ost_handler.c
+++ b/lustre/ost/ost_handler.c
@@ -600,6 +600,11 @@ int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 GOTO(out, rc);
         rcs = lustre_msg_buf(req->rq_repmsg, 1, niocount * sizeof(*rcs));
 
+        /* Do snap options here*/
+        rc = obd_do_cow(req->rq_export, ioo, objcount, remote_nb);
+        if (rc)
+                GOTO(out, rc = npages); 
+
         /* FIXME all niobuf splitting should be done in obdfilter if needed */
         /* CAVEAT EMPTOR this sets ioo->ioo_bufcnt to # pages */
         npages = get_per_page_niobufs(ioo, objcount,remote_nb,niocount,&pp_rnb);
@@ -705,11 +710,12 @@ int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 }
                 LASSERT(j == npages);
         }
-        rc = obd_write_extents(req->rq_export, ioo, npages, local_nb, rc);
+        rc = obd_write_extents(req->rq_export, ioo, objcount, niocount, 
+                               local_nb, rc);
         if (rc) {
                 CERROR("write extents error of id "LPU64" rc=%d\n", 
                         ioo->ioo_id, rc);  
-        } 
+        }
  out_bulk:
         ptlrpc_free_bulk(desc);
  out_local:
diff --git a/lustre/smfs/dir.c b/lustre/smfs/dir.c
index e3ce2ab323edb47ba210e1b03a1c280f8e10a43b..7fc58f36c72e75d0bd55fbe63102c0ae3d2cd9bf 100644
--- a/lustre/smfs/dir.c
+++ b/lustre/smfs/dir.c
@@ -34,6 +34,7 @@
 #include <linux/lustre_lib.h>
 #include <linux/lustre_idl.h>
 #include <linux/lustre_fsfilt.h>
+#include <linux/lustre_snap.h>
 #include <linux/lustre_smfs.h>
 
 #include "smfs_internal.h"
@@ -74,10 +75,10 @@ static int smfs_create(struct inode *dir, struct dentry *dentry,
         if (!cache_dentry || !cache_parent)
                 GOTO(exit, rc = -ENOMEM);
 
-        pre_smfs_inode(dir, cache_dir);
-
-        SMFS_PRE_COW(dir, dentry, REINT_CREATE, "create", rc, exit);
 
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_CREATE, "create", rc, exit);
+        
+        pre_smfs_inode(dir, cache_dir);
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         if (cache_dir && cache_dir->i_op->create)
                 rc = cache_dir->i_op->create(cache_dir, cache_dentry,
@@ -209,7 +210,7 @@ static int smfs_link(struct dentry * old_dentry,
         
         lock_kernel();
         
-        SMFS_PRE_COW(dir, old_dentry, REINT_LINK, "link", rc, exit);
+        SMFS_PRE_COW(dir, old_dentry, NULL, NULL, REINT_LINK, "link", rc, exit);
         
         cache_parent = pre_smfs_dentry(NULL, cache_dir, dentry);
         cache_dentry = pre_smfs_dentry(cache_parent, NULL, dentry);
@@ -270,7 +271,7 @@ static int smfs_unlink(struct inode * dir,
 
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_UNLINK, handle, dir, rc);
 
-        SMFS_PRE_COW(dir, dentry, REINT_UNLINK, "unlink", rc, exit);
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_UNLINK, "unlink", rc, exit);
         
         cache_parent = pre_smfs_dentry(NULL, cache_dir, dentry);
         cache_dentry = pre_smfs_dentry(cache_parent, cache_inode, dentry);
@@ -323,6 +324,9 @@ static int smfs_symlink(struct inode *dir, struct dentry *dentry,
 
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_SYMLINK, handle, dir, rc);
 
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_CREATE, "symlink", rc, 
+                     exit);
+        
         pre_smfs_inode(dir, cache_dir);
         lock_kernel();
         if (cache_dir->i_op->symlink)
@@ -369,6 +373,8 @@ static int smfs_mkdir(struct inode *dir, struct dentry *dentry,
 
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_MKDIR, handle, dir, rc);
 
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_CREATE, "mkdir", rc, 
+                     exit);
         cache_parent = pre_smfs_dentry(NULL, cache_dir, dentry);
         cache_dentry = pre_smfs_dentry(cache_parent, NULL, dentry);
 
@@ -425,6 +431,8 @@ static int smfs_rmdir(struct inode *dir, struct dentry *dentry)
 
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_RMDIR, handle, dir, rc);
 
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_UNLINK, "rmdir", rc, exit);
+
         cache_parent = pre_smfs_dentry(NULL, cache_dir, dentry);
         cache_dentry = pre_smfs_dentry(cache_parent, cache_inode, dentry);
 
@@ -478,6 +486,7 @@ static int smfs_mknod(struct inode *dir, struct dentry *dentry,
         }
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_MKNOD, handle, dir, rc);
 
+        SMFS_PRE_COW(dir, dentry, NULL, NULL, REINT_CREATE, "mknod", rc, exit);
         cache_parent = pre_smfs_dentry(NULL, cache_dir, dentry->d_parent);
         cache_dentry = pre_smfs_dentry(cache_parent, NULL, dentry);
         lock_kernel();
@@ -541,6 +550,8 @@ static int smfs_rename(struct inode * old_dir, struct dentry *old_dentry,
         }
         lock_kernel();
 
+        SMFS_PRE_COW(old_dir, old_dentry, new_dir, new_dentry, REINT_RENAME, 
+                     "rename", rc, exit);
         SMFS_CACHE_HOOK_PRE(CACHE_HOOK_RENAME, handle, old_dir, rc);
 
         cache_old_parent = pre_smfs_dentry(NULL, cache_old_dir, old_dentry);
@@ -561,7 +572,7 @@ static int smfs_rename(struct inode * old_dir, struct dentry *old_dentry,
         if (cache_old_dir->i_op->rename)
                 rc = cache_old_dir->i_op->rename(cache_old_dir, cache_old_dentry,
                                                  cache_new_dir, cache_new_dentry);
-
+        
         post_smfs_inode(old_dir, cache_old_dir);
         post_smfs_inode(new_dir, cache_new_dir);
 
diff --git a/lustre/smfs/file.c b/lustre/smfs/file.c
index af1e1f5f7d50f5f2d8da722726ab3eec077886e5..d7f325ed6410e37a2fa04cfa702ba3ee99fa6bc3 100644
--- a/lustre/smfs/file.c
+++ b/lustre/smfs/file.c
@@ -39,6 +39,7 @@
 #include <linux/lustre_idl.h>
 #include <linux/lustre_fsfilt.h>
 #include <linux/lustre_smfs.h>
+#include <linux/lustre_snap.h>
 
 #include "smfs_internal.h"
 
@@ -62,6 +63,15 @@ static ssize_t smfs_write(struct file *filp, const char *buf, size_t count,
         if (sfi->magic != SMFS_FILE_MAGIC) 
                 LBUG();
 
+        if (filp->f_flags & O_APPEND)
+                tmp_ppos = filp->f_dentry->d_inode->i_size;
+        else {
+                tmp_ppos = *ppos;
+        }
+
+        SMFS_PRE_COW(filp->f_dentry->d_inode, filp->f_dentry, &count, &tmp_ppos, 
+                     REINT_WRITE, "write", rc, exit);  
+
         if (ppos != &(filp->f_pos)) {
                 cache_ppos = &tmp_ppos;
         } else {
diff --git a/lustre/smfs/inode.c b/lustre/smfs/inode.c
index 73b9a88f4a8fb92b6da945d4d8c46f85fb530d03..8360b18bb5756c8b58226c8f3d5ddabd52272f20 100644
--- a/lustre/smfs/inode.c
+++ b/lustre/smfs/inode.c
@@ -192,10 +192,10 @@ static void smfs_write_inode(struct inode *inode, int wait)
                 return;
         }
         pre_smfs_inode(inode, cache_inode);
-
         if (S2CSB(inode->i_sb)->s_op->write_inode)
                 S2CSB(inode->i_sb)->s_op->write_inode(cache_inode, wait);
-
+        
+        post_smfs_inode(inode, cache_inode);
         EXIT;
 }
 
diff --git a/lustre/smfs/smfs_cow.c b/lustre/smfs/smfs_cow.c
index 3e44317222a34293709fbfff28d0cd3f13aa6273..5b4f5798b8fddc92ac0ff319c485d73493a41923 100644
--- a/lustre/smfs/smfs_cow.c
+++ b/lustre/smfs/smfs_cow.c
@@ -24,6 +24,7 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/pagemap.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
@@ -443,7 +444,8 @@ int snap_do_cow(struct inode *inode, struct dentry *dparent, int del)
         RETURN(0);
 }
 /*Dir inode will do cow*/
-int smfs_cow_create(struct inode *dir, struct dentry *dentry)
+int smfs_cow_create(struct inode *dir, struct dentry *dentry,
+                    void *data1, void *data2)
 {
         int rc = 0;
         struct dentry *dparent;
@@ -461,7 +463,8 @@ int smfs_cow_create(struct inode *dir, struct dentry *dentry)
         RETURN(rc);
 }
 
-int smfs_cow_setattr(struct inode *dir, struct dentry *dentry)
+int smfs_cow_setattr(struct inode *dir, struct dentry *dentry,
+                     void *data1, void *data2)
 {
         int rc = 0;
         ENTRY;
@@ -475,7 +478,8 @@ int smfs_cow_setattr(struct inode *dir, struct dentry *dentry)
         RETURN(rc);
 }
 
-int smfs_cow_link(struct inode *dir, struct dentry *dentry)
+int smfs_cow_link(struct inode *dir, struct dentry *dentry,
+                  void *data1, void *data2)
 {
         int rc = 0;
         struct dentry *dparent;
@@ -497,7 +501,8 @@ int smfs_cow_link(struct inode *dir, struct dentry *dentry)
         RETURN(rc);
 }
 
-int smfs_cow_unlink(struct inode *dir, struct dentry *dentry)
+int smfs_cow_unlink(struct inode *dir, struct dentry *dentry,
+                    void *data1, void *data2)
 {
         struct dentry *dparent;
         int rc = 0;
@@ -520,23 +525,123 @@ int smfs_cow_unlink(struct inode *dir, struct dentry *dentry)
         RETURN(rc);
 }
 
-int smfs_cow_rename(struct inode *dir, struct dentry *dentry)
+int smfs_cow_rename(struct inode *dir, struct dentry *dentry, 
+                    void *data1, void *data2)
 {
+        struct inode *new_dir = (struct inode *)data1;
+        struct dentry *new_dentry = (struct dentry *)data2;
+        struct dentry *dparent;
         int rc = 0;
         ENTRY;
-        
+       
+        LASSERT(new_dir);
+        LASSERT(new_dentry); 
+        if (smfs_needs_cow(dir) != -1) {
+		CDEBUG(D_INODE, "snap_needs_cow for ino %lu \n", dir->i_ino);
+                LASSERT(dentry->d_parent && dentry->d_parent->d_parent);
+                dparent = dentry->d_parent->d_parent;
+		if ((snap_do_cow(dir, dparent, 0))) {
+			CERROR("Do cow error\n");
+			RETURN(-EINVAL);
+		}
+               	if ((snap_do_cow(dentry->d_inode, dentry->d_parent, 0))) {
+			CERROR("Do cow error\n");
+			RETURN(-EINVAL);
+                }
+        }
+        if (smfs_needs_cow(new_dir) != -1) {
+        	CDEBUG(D_INODE, "snap_needs_cow for ino %lu \n", new_dir->i_ino);
+                LASSERT(new_dentry->d_parent && new_dentry->d_parent->d_parent);
+                dparent = new_dentry->d_parent->d_parent;
+		if ((new_dir != dir) && (snap_do_cow(new_dir, dparent, 0))){
+			CERROR("Do cow error\n");
+			RETURN(-EINVAL);
+		}
+                if (new_dentry->d_inode && new_dentry->d_inode->i_nlink == 1) {
+               	        if ((snap_do_cow(new_dentry->d_inode, 
+                                         new_dentry->d_parent, 0))) {
+			        CERROR("Do cow error\n");
+			        RETURN(-EINVAL);
+                        }
+                }
+        } 
         RETURN(rc);
 }
 
-int smfs_cow_write(struct inode *dir, struct dentry *dentry)
+int smfs_cow_write(struct inode *inode, struct dentry *dentry, void *data1,
+                   void *data2)
 {
-        int rc = 0;
+        struct snap_info *snap_info = S2SNAPI(inode->i_sb); 
+        struct snap_table *table = snap_info->sntbl; 
+	long   blocks[2]={-1,-1};
+       	int  index = 0, i, rc = 0;
+        size_t count = *(size_t *)data1;
+	loff_t pos = *(loff_t*)data2;
+
         ENTRY;
+
+        LASSERT(count);
+        LASSERT(pos);
+ 
+	down(&inode->i_sem);
         
+        if (smfs_needs_cow(inode) != -1 ) {
+                CDEBUG(D_INFO, "snap_needs_cow for ino %lu \n",inode->i_ino);
+                snap_do_cow(inode, dentry->d_parent, 0);
+	}
+	
+	CDEBUG(D_INFO, "write offset %lld count %u \n", pos, count);
+	
+	if(pos & (PAGE_CACHE_SIZE - 1)){
+	        blocks[0] = pos >> inode->i_sb->s_blocksize_bits;
+        }
+	pos += count - 1;
+	if((pos + 1) & (PAGE_CACHE_SIZE - 1)){
+	        blocks[1] = pos >> inode->i_sb->s_blocksize_bits;
+	}
+
+	if (blocks[0] == blocks[1]) 
+                blocks[1] = -1;
+	
+        for (i = 0; i < 2; i++) {
+		int slot = 0;
+                if (blocks[i] == -1) 
+			continue;
+		/*Find the nearest page in snaptable and copy back it*/
+		for (slot = table->sntbl_count - 1; slot >= 0; slot--) {
+                        struct fsfilt_operations *snapops = snap_info->snap_fsfilt;
+			struct inode *cache_inode = NULL;
+               		int result = 0;
+
+                        index = table->sntbl_items[slot].sn_index;
+			cache_inode = snapops->fs_get_indirect(inode, NULL, index);
+
+			if (!cache_inode)  continue;
+
+			CDEBUG(D_INFO, "find cache_ino %lu\n", cache_inode->i_ino);
+		
+			result = snapops->fs_copy_block(inode, cache_inode, blocks[i]);
+			if (result == 1) {
+               			iput(cache_inode);
+				result = 0;
+				break;
+			}
+			if (result < 0) {
+				iput(cache_inode);
+				up(&inode->i_sem);
+				GOTO(exit, rc = result);
+			}
+               		iput(cache_inode);
+        	}
+	}
+exit:
+        up(&inode->i_sem); 
         RETURN(rc);
 }
+EXPORT_SYMBOL(smfs_cow_write);
 
-typedef int (*cow_funcs)(struct inode *dir, struct dentry *dentry);
+typedef int (*cow_funcs)(struct inode *dir, struct dentry *dentry, 
+                         void *new_dir, void *new_dentry);
 
 static cow_funcs smfs_cow_funcs[REINT_MAX + 1] = {
         [REINT_SETATTR] smfs_cow_setattr,
@@ -547,8 +652,9 @@ static cow_funcs smfs_cow_funcs[REINT_MAX + 1] = {
         [REINT_WRITE]   smfs_cow_write,
 };
 
-int smfs_cow(struct inode *dir, struct dentry *dentry, int op)
+int smfs_cow(struct inode *dir, struct dentry *dentry, void *new_dir, 
+             void *new_dentry, int op)
 {
-        return smfs_cow_funcs[op](dir, dentry);
+        return smfs_cow_funcs[op](dir, dentry, new_dir, new_dentry);
 }
 
diff --git a/lustre/smfs/smfs_internal.h b/lustre/smfs/smfs_internal.h
index 1c72bf69b021012c6775b701e5c2f99ab874f76d..cb5862a816634962cf30cbd5a6d0bad8ed9b13e4 100644
--- a/lustre/smfs/smfs_internal.h
+++ b/lustre/smfs/smfs_internal.h
@@ -315,21 +315,22 @@ static inline int get_active_entry(struct inode *dir, __u64 *active_entry)
 }
 #if CONFIG_SNAPFS
 /*snap macros*/
-#define SMFS_PRE_COW(dir, dentry, op, name, rc, label)                  \
-do {                                                                    \
-        if (smfs_do_cow(dir) && !rc) {                                  \
-                CDEBUG(D_INODE, "Do %s snap post for dir %lu \n",       \
-                              name, dir->i_ino);                        \
-                rc = smfs_cow(dir, dentry, op);                         \
-                if (rc)                                                 \
-                        GOTO(label, rc);                                \
-        }                                                               \
+#define SMFS_PRE_COW(dir, dentry, new_dir, new_dentry, op, name, rc, label)    \
+do {                                                                           \
+        if (smfs_do_cow(dir) && !rc) {                                         \
+                CDEBUG(D_INODE, "Do %s snap post for dir %lu \n",              \
+                              name, dir->i_ino);                               \
+                rc = smfs_cow(dir, dentry, new_dir, new_dentry, op);           \
+                if (rc)                                                        \
+                        GOTO(label, rc);                                       \
+        }                                                                      \
 } while(0)
+
 extern int smfs_cow_init(struct super_block *sb);
 extern int smfs_cow_cleanup(struct super_block *sb);
 extern int smfs_init_snap_inode_info(struct inode *inode, int flags);
 #else
-#define SMFS_PRE_COW(dir, dentry, op, name, rc, label)                 
+#define SMFS_PRE_COW(dir, dentry, new_dir, new_dentry, op, name, rc, label)                 
 #endif 
 
 #endif /*__KERNEL*/