X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fsmfs%2Fsmfs_cow.c;h=9fe77a6012c6a4254201f289e55b5587315c1d76;hb=a74d5df65aa887939138ea07dc7a23d0d293f7e0;hp=b592087460da08f4ab1724c9819f8d7d81fc4c05;hpb=7088f04601ade93b6bba2c44cc9c98af485c8e86;p=fs%2Flustre-release.git diff --git a/lustre/smfs/smfs_cow.c b/lustre/smfs/smfs_cow.c index b592087..9fe77a6 100644 --- a/lustre/smfs/smfs_cow.c +++ b/lustre/smfs/smfs_cow.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -35,182 +36,572 @@ #include #include -#include #include +#include #include "smfs_internal.h" -#define SNAPTABLE_SIZE(size) (sizeof(struct snap_table) + size * sizeof(struct snap)) -static int smfs_init_snaptabe(struct super_block *sb) + +#define SNAPTABLE_SIZE(size) (sizeof(struct snap_table) + \ + size * sizeof(struct snap)) + +int smfs_cleanup_snap_info(struct snap_info *snap_info); + +static int smfs_init_snap_super_info(struct smfs_super_info *smfs_info) +{ + struct snap_super_info *snap_sinfo; + int rc = 0; + + ENTRY; + + OBD_ALLOC(smfs_info->smsi_snap_info, + sizeof(struct snap_super_info)); + + if (!smfs_info->smsi_snap_info) + GOTO(exit, rc = -ENOMEM); + + snap_sinfo = smfs_info->smsi_snap_info; + + /*init snap fsfilt operations*/ + if (!snap_sinfo->snap_cache_fsfilt) { + char *snap_cache_ftype = NULL; + int tmp = strlen(smfs_info->smsi_cache_ftype) + strlen("_snap"); + + OBD_ALLOC(snap_cache_ftype, tmp + 1); + sprintf(snap_cache_ftype, "%s_snap", smfs_info->smsi_cache_ftype); + snap_sinfo->snap_cache_fsfilt = fsfilt_get_ops(snap_cache_ftype); + OBD_FREE(snap_cache_ftype, tmp + 1); + if (!snap_sinfo->snap_cache_fsfilt) { + CERROR("Can not get %s fsfilt ops needed by snap\n", + snap_cache_ftype); + GOTO(exit, rc = -EINVAL); + } + } + if (!snap_sinfo->snap_fsfilt) { + char *snap_ftype = NULL; + int tmp = strlen(smfs_info->smsi_ftype) + strlen("_snap"); + + OBD_ALLOC(snap_ftype, tmp + 1); + sprintf(snap_ftype, "%s_snap", smfs_info->smsi_ftype); + snap_sinfo->snap_fsfilt = fsfilt_get_ops(snap_ftype); + OBD_FREE(snap_ftype, tmp + 1); + if (!snap_sinfo->snap_fsfilt) { + CERROR("Can not get %s fsfilt ops needed by snap\n", + snap_ftype); + GOTO(exit, rc = -EINVAL); + } + } + INIT_LIST_HEAD(&snap_sinfo->snap_list); +exit: + if (rc && smfs_info->smsi_snap_info) + OBD_FREE(snap_sinfo, sizeof(struct snap_super_info)); + RETURN(rc); +} +/*FIXME-wangdi Should remove it when integrated it with lustre*/ +static struct dentry *smfs_simple_mkdir(struct dentry *dir, char *name, + int mode, int fix) +{ + struct dentry *dchild; + int err = 0; + + dchild = ll_lookup_one_len(name, dir, strlen(name)); + if (IS_ERR(dchild)) + GOTO(out_up, dchild); + + if (dchild->d_inode) { + int old_mode = dchild->d_inode->i_mode; + if (!S_ISDIR(old_mode)) + GOTO(out_err, err = -ENOTDIR); + + /* Fixup directory permissions if necessary */ + if (fix && (old_mode & S_IALLUGO) != (mode & S_IALLUGO)) { + CWARN("fixing permissions on %s from %o to %o\n", + name, old_mode, mode); + dchild->d_inode->i_mode = (mode & S_IALLUGO) | + (old_mode & ~S_IALLUGO); + mark_inode_dirty(dchild->d_inode); + } + GOTO(out_up, dchild); + } + err = vfs_mkdir(dir->d_inode, dchild, mode); + if (err) + GOTO(out_err, err); + RETURN(dchild); +out_err: + dput(dchild); + dchild = ERR_PTR(err); +out_up: + return dchild; + +} +static struct snap_info *smfs_find_snap_info(struct inode *inode) +{ + struct snap_inode_info *snap_iinfo = I2SNAPI(inode); + struct snap_super_info *snap_sinfo = S2SNAPI(inode->i_sb); + struct snap_info *snap_info = NULL, *tmp; + + ENTRY; + list_for_each_entry_safe(snap_info, tmp, &snap_sinfo->snap_list, + sni_list) { + if (snap_info->sni_root_ino == snap_iinfo->sn_root_ino) + RETURN(snap_info); + } + RETURN(NULL); +} + +#if 0 +static int smfs_dotsnap_dir_size(struct inode *inode) +{ + struct snap_super_info *snap_sinfo = S2SNAPI(inode->i_sb); + struct fsfilt_operations *snapops = snap_sinfo->snap_cache_fsfilt; + int size = 0, dir_size = 0, blocks, i = 0; + struct snap_table *snap_table = NULL; + struct snap_info *snap_info = NULL; + ENTRY; + + snap_info = smfs_find_snap_info(inode); + + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for inode %p\n", inode); + RETURN(0); + } + snap_table = snap_info->sni_table; + for (i = 0; i < snap_table->sntbl_count; i++) { + char *name = snap_table->sntbl_items[i].sn_name; + size += snapops->fs_dir_ent_size(name); + } + /*FIXME this is only for ext3 dir format, may need fix for other FS*/ + blocks = (size + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; + + dir_size = blocks * inode->i_sb->s_blocksize; + RETURN(dir_size); + +} +#endif + +static int smfs_init_snap_inode_info(struct inode *inode, struct inode *dir, int index) +{ + int rc = 0; + ENTRY; + + if (!inode) + RETURN(0); + + if (dir) { + I2SNAPI(inode)->sn_flags = I2SNAPI(dir)->sn_flags; + I2SNAPI(inode)->sn_gen = I2SNAPI(dir)->sn_gen; + I2SNAPI(inode)->sn_root_ino = I2SNAPI(dir)->sn_root_ino; + I2SNAPI(inode)->sn_index = I2SNAPI(inode)->sn_index; + } else { + I2SNAPI(inode)->sn_flags = 0; + I2SNAPI(inode)->sn_gen = 0; + } + + I2SNAPI(inode)->sn_index = index; + + if (smfs_dotsnap_inode(inode)) { + struct snap_info *snap_info; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + RETURN(-EIO); + } + /*init dot_snap inode info*/ +// inode->i_size = (loff_t)smfs_dotsnap_dir_size(inode); + inode->i_size = snap_info->sni_table->sntbl_count; + inode->i_nlink = snap_info->sni_table->sntbl_count + 2; + inode->i_uid = 0; + inode->i_gid = 0; + } else if (SMFS_DO_COW(S2SMI(inode->i_sb)) && + (I2SMI(inode)->smi_flags & SM_DO_COW) && + smfs_primary_inode(inode)) { + struct snap_inode_info *sni_info = I2SNAPI(inode); + struct fsfilt_operations *sops = I2SNAPCOPS(inode); + int vallen = 0; + + vallen = sizeof(sni_info->sn_gen); + + rc = sops->fs_get_snap_info(I2CI(inode), SNAP_GENERATION, + strlen(SNAP_GENERATION), + &sni_info->sn_gen, &vallen); + } + RETURN(rc); +} + +#define COWED_NAME_LEN (7 + 8 + 1) +static int smfs_init_cowed_dir(struct snap_info *snap_info, struct inode* inode) +{ + struct dentry *dentry = NULL; + char name[COWED_NAME_LEN]; + int rc = 0; + ENTRY; + + sprintf(name, ".cowed_%08x", (__u32)inode->i_ino); + /*FIXME-WANGDI: will use simple_mkdir, when integrating snap to lustre*/ + dentry = smfs_simple_mkdir(inode->i_sb->s_root, name, 0777, 1); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); + CERROR("create cowed directory: rc = %d\n", rc); + RETURN(rc); + } + snap_info->sni_cowed_dentry = dentry; + /*cleanup cowed inode attr for cowed dir*/ + SMFS_CLEAN_INODE_COWED(dentry->d_inode); + RETURN(rc); +} + +static int smfs_init_dotinfo(struct snap_info *snap_info) +{ + struct snap_dot_info *dot_info = NULL; + int rc = 0; + ENTRY; + + if (snap_info->sni_dot_info) + RETURN(-EEXIST); + + OBD_ALLOC(snap_info->sni_dot_info, sizeof(struct snap_dot_info)); + + if (!snap_info->sni_dot_info) + RETURN(-ENOMEM); + + dot_info = snap_info->sni_dot_info; + + OBD_ALLOC(dot_info->dot_name, strlen(DOT_SNAP_NAME) + 1); + + if (!dot_info->dot_name) { + OBD_FREE(snap_info->sni_dot_info, sizeof(struct snap_dot_info)); + RETURN(-ENOMEM); + } + memcpy(dot_info->dot_name, DOT_SNAP_NAME, strlen(DOT_SNAP_NAME)); + + dot_info->dot_name_len = strlen(DOT_SNAP_NAME); + dot_info->dot_snap_enable = 1; + + RETURN(rc); +} + +static int smfs_init_snap_info(struct smfs_super_info *smb, + struct snap_info *snap_info, struct inode *inode) { - struct snap_info *snap_info = S2SNAPI(sb); struct snap_table *snap_table = NULL; - struct fsfilt_operations *snapops = snap_info->snap_fsfilt; - int rc = 0, size, table_size, vallen; + struct fsfilt_operations *snapcops; + int rc = 0, size, table_size, vallen, i; ENTRY; - init_MUTEX(&snap_info->sntbl_sema); + snapcops = smb->smsi_snap_info->snap_cache_fsfilt; /*Initialized table */ /*get the maxsize of snaptable*/ vallen = sizeof(int); - rc = snapops->fs_get_snap_info(sb, NULL, MAX_SNAPTABLE_COUNT, + rc = snapcops->fs_get_snap_info(I2CI(inode), MAX_SNAPTABLE_COUNT, strlen(MAX_SNAPTABLE_COUNT), &size, &vallen); if (size == 0) { CERROR("the Max snaptable count should not be zero\n"); - RETURN(-EINVAL); + GOTO(exit, rc); } - table_size = SNAPTABLE_SIZE(size); - OBD_ALLOC(snap_info->sntbl, table_size); + OBD_ALLOC(snap_info->sni_table, table_size); - if (!snap_info->sntbl) { + if (!snap_info->sni_table) { CERROR("No MEM\n"); RETURN(-ENOMEM); } - snap_table = snap_info->sntbl; + snap_table = snap_info->sni_table; snap_table->sntbl_magic = cpu_to_le32((__u32)SNAPTABLE_MAGIC); snap_table->sntbl_max_count = size; + /*init sn_index to -1*/ + for (i = 0; i < snap_table->sntbl_max_count; i++) + snap_table->sntbl_items[i].sn_index = -1; /*get snaptable info*/ - - rc = snapops->fs_get_snap_info(sb, NULL, SNAPTABLE_INFO, - strlen(SNAPTABLE_INFO), - snap_table, &table_size); + rc = snapcops->fs_get_snap_info(I2CI(inode), SNAPTABLE_INFO, + strlen(SNAPTABLE_INFO), + snap_table, &table_size); if (rc < 0) { - if (rc == -ENOATTR) { + if (rc == -ENODATA) { snap_table->sntbl_count = 0; - CDEBUG(D_INFO, "No snaptable here\n"); - RETURN(0); + rc = 0; } else { CERROR("Can not retrive the snaptable from this filesystem\n"); - OBD_FREE(snap_table, table_size); - RETURN(rc); + GOTO(exit, rc); } - } - if (le32_to_cpu(snap_table->sntbl_magic) != SNAPTABLE_MAGIC) { - CERROR("On disk snaptable is not right \n"); + } else { + if (le32_to_cpu(snap_table->sntbl_magic) != SNAPTABLE_MAGIC) { + CERROR("On disk snaptable is not right \n"); + GOTO(exit, rc = -EIO); + } + } + init_MUTEX(&snap_info->sni_sema); + snap_info->sni_root_ino = inode->i_ino; + rc = smfs_init_cowed_dir(snap_info, inode); + if (rc) { + CERROR("Init cowed dir error rc=%d\n", rc); + GOTO(exit, rc); + } + rc = smfs_init_dotinfo(snap_info); +exit: + if (rc && snap_table) OBD_FREE(snap_table, table_size); - RETURN(-EIO); + RETURN(rc); +} + +static struct snap_info *smfs_create_snap_info(struct smfs_super_info *sinfo, + struct inode *inode) +{ + struct snap_info *snap_info = NULL; + int rc = 0; + ENTRY; + + OBD_ALLOC(snap_info, sizeof(struct snap_info)); + if (!snap_info) + RETURN(ERR_PTR(-ENOMEM)); + rc = smfs_init_snap_info(sinfo, snap_info, inode); + if (rc) + GOTO(exit, rc); + + /*set cow flags for the snap root inode*/ + I2SMI(inode)->smi_flags |= SM_DO_COW; + I2SNAPI(inode)->sn_root_ino = inode->i_ino; +exit: + if (rc) { + OBD_FREE(snap_info, sizeof(struct snap_info)); + snap_info = ERR_PTR(rc); } + RETURN(snap_info); +} + +static int smfs_cow_pre(struct inode *dir, void *dentry, void *new_dir, + void *new_dentry, int op); + +static int smfs_cow_post(struct inode *dir, void *dentry, void *new_dir, + void *new_dentry, int op); +#define COW_HOOK "cow_hook" +static int smfs_cow_pre_hook(struct inode *inode, void *dentry, void *data1, + void *data2, int op, void *handle) +{ + int rc = 0; + ENTRY; + + if (smfs_do_cow(inode)) { + /*FIXME:WANGDI, get index from the dentry*/ + #if 0 + int index = 0; + smfs_get_dentry_name_index(dentry, &name, index); + smfs_free_dentry_name(&name); + #endif + rc = smfs_cow_pre(inode, dentry, data1, data2, op); + } + RETURN(rc); +} +static int smfs_cow_post_hook(struct inode *inode, void *dentry, void *data1, + void *data2, int op, void *handle) +{ + int rc = 0; + ENTRY; + + if (smfs_do_cow(inode)) { + rc = smfs_cow_post(inode, dentry, data1, data2, op); + } + RETURN(rc); +} + +int smfs_cow_cleanup(struct smfs_super_info *smb) +{ + struct snap_super_info *snap_sinfo = smb->smsi_snap_info; + struct list_head *snap_list = &snap_sinfo->snap_list; + struct smfs_hook_ops *cow_hops; + int rc = 0; + ENTRY; + + while (!list_empty(snap_list)) { + struct snap_info *snap_info; + + snap_info = list_entry(snap_list->next, struct snap_info, + sni_list); + rc = smfs_cleanup_snap_info(snap_info); + if (rc) + CERROR("cleanup snap_info error rc=%d\n", rc); + list_del(&snap_info->sni_list); + OBD_FREE(snap_info, sizeof(struct snap_info)); + } + + if (snap_sinfo->snap_fsfilt) + fsfilt_put_ops(snap_sinfo->snap_fsfilt); + if (snap_sinfo->snap_cache_fsfilt) + fsfilt_put_ops(snap_sinfo->snap_cache_fsfilt); + + cow_hops = smfs_unregister_hook_ops(smb, COW_HOOK); + smfs_free_hook_ops(cow_hops); + + SMFS_CLEAN_COW(smb); + if (snap_sinfo) + OBD_FREE(snap_sinfo, sizeof(struct snap_super_info)); RETURN(rc); } int smfs_cow_init(struct super_block *sb) { struct smfs_super_info *smfs_info = S2SMI(sb); - struct inode *inode = sb->s_root->d_inode; - int rc = 0; + struct smfs_hook_ops *cow_hops = NULL; + struct fsfilt_operations *sops; + struct inode *root_inode = smfs_info->smsi_sb->s_root->d_inode; + int snap_count = 0, rc = 0, vallen; + + ENTRY; SMFS_SET_COW(smfs_info); - OBD_ALLOC(smfs_info->smsi_snap_info, sizeof(struct snap_info)); - - if (!smfs_info->smsi_snap_info) + cow_hops = smfs_alloc_hook_ops(COW_HOOK, smfs_cow_pre_hook, + smfs_cow_post_hook); + if (!cow_hops) { RETURN(-ENOMEM); + } + + rc = smfs_register_hook_ops(smfs_info, cow_hops); + if (rc) { + smfs_free_hook_ops(cow_hops); + RETURN(rc); + } - /*init snap fsfilt operations*/ - if (!S2SNAPI(sb)->snap_cache_fsfilt) { - char *snap_cache_ftype = NULL; - int tmp = strlen(S2SMI(sb)->smsi_cache_ftype) + strlen("_snap"); - - OBD_ALLOC(snap_cache_ftype, tmp + 1); - sprintf(snap_cache_ftype, "%s_snap", S2SMI(sb)->smsi_cache_ftype); - S2SNAPI(sb)->snap_cache_fsfilt = fsfilt_get_ops(snap_cache_ftype); - OBD_FREE(snap_cache_ftype, tmp + 1); - if (!S2SNAPI(sb)->snap_cache_fsfilt) { - CERROR("Can not get %s fsfilt ops needed by snap\n", - snap_cache_ftype); - RETURN(-EINVAL); - } + rc = smfs_init_snap_super_info(smfs_info); + if (rc && cow_hops) { + smfs_unregister_hook_ops(smfs_info, cow_hops->smh_name); + smfs_free_hook_ops(cow_hops); + RETURN(rc); } - if (!S2SNAPI(sb)->snap_fsfilt) { - char *snap_ftype = NULL; - int tmp = strlen(S2SMI(sb)->smsi_ftype) + strlen("_snap"); + sops = smfs_info->smsi_snap_info->snap_cache_fsfilt; + + vallen = sizeof(int); + rc = sops->fs_get_snap_info(root_inode, SNAP_COUNT, strlen(SNAP_COUNT), + &snap_count, &vallen); + if (rc < 0) + GOTO(exit, rc); + + if (snap_count > 0) { + int snap_root_size = snap_count * sizeof(ino_t); + ino_t *snap_root; + int i; - OBD_ALLOC(snap_ftype, tmp + 1); - sprintf(snap_ftype, "%s_snap", S2SMI(sb)->smsi_ftype); - S2SNAPI(sb)->snap_fsfilt = fsfilt_get_ops(snap_ftype); - OBD_FREE(snap_ftype, tmp + 1); - if (!S2SNAPI(sb)->snap_fsfilt) { - CERROR("Can not get %s fsfilt ops needed by snap\n", - snap_ftype); - RETURN(-EINVAL); + OBD_ALLOC(snap_root, snap_root_size); + + if (!snap_root) + GOTO(exit, rc = -ENOMEM); + + rc = sops->fs_get_snap_info(root_inode, SNAP_ROOT_INO, + strlen(SNAP_ROOT_INO), snap_root, + &snap_root_size); + if (rc < 0) { + OBD_FREE(snap_root, sizeof(int) * snap_count); + GOTO(exit, rc); } - } - rc = smfs_init_snaptabe(sb); - + for (i = 0; i < snap_count; i++) { + ino_t root_ino = le32_to_cpu(snap_root[i]); + struct snap_info *snap_info; + + root_inode = smfs_get_inode(sb, root_ino, NULL, 0); + smfs_init_snap_inode_info(root_inode, NULL, 0); + snap_info = smfs_create_snap_info(S2SMI(sb), root_inode); + iput(root_inode); + if (IS_ERR(snap_info)) { + OBD_FREE(snap_root, sizeof(int) * snap_count); + GOTO(exit, rc = PTR_ERR(snap_info)); + } + list_add(&snap_info->sni_list, + &(S2SNAPI(sb)->snap_list)); + } + } + smfs_info->smsi_snap_info->snap_count = snap_count; +exit: + if (rc) + smfs_cow_cleanup(smfs_info); RETURN(rc); } -int smfs_cow_cleanup(struct super_block *sb) -{ - struct smfs_super_info *smfs_info = S2SMI(sb); - struct snap_info *snap_info = S2SNAPI(sb); - struct snap_table *snap_table = snap_info->sntbl; - int rc = 0, table_size; +static int smfs_cleanup_dotinfo(struct snap_info *snap_info) +{ + struct snap_dot_info *dot_info = NULL; + int rc = 0; ENTRY; - SMFS_CLEAN_COW(smfs_info); + if (!snap_info->sni_dot_info) + RETURN(rc); - if (snap_info->snap_fsfilt) - fsfilt_put_ops(snap_info->snap_fsfilt); - if (snap_info->snap_cache_fsfilt) - fsfilt_put_ops(snap_info->snap_cache_fsfilt); + dot_info = snap_info->sni_dot_info; - if (snap_table) { - table_size = SNAPTABLE_SIZE(snap_table->sntbl_max_count); - OBD_FREE(snap_info->sntbl, table_size); + if (dot_info->dot_name) { + OBD_FREE(dot_info->dot_name, dot_info->dot_name_len + 1); } - if (snap_info) - OBD_FREE(snap_info, sizeof(*snap_info)); + OBD_FREE(dot_info, sizeof(struct snap_dot_info)); + RETURN(rc); } -/*FIXME Note indirect and primary inode -* should be recorgnized here*/ -int smfs_init_snap_inode_info(struct inode *inode, int flags) +int smfs_cleanup_snap_info(struct snap_info *snap_info) { - struct snap_inode_info *sni_info = I2SNAPI(inode); - struct fsfilt_operations *snapops = I2SNAPOPS(inode); - int vallen, rc = 0; + struct snap_table *snap_table = snap_info->sni_table; + int rc = 0, table_size; ENTRY; - if (SMFS_DO_COW(S2SMI(inode->i_sb)) && - (flags & SM_DO_COW)) { - sni_info->sn_flags = flags; - vallen = sizeof(sni_info->sn_gen); + l_dput(snap_info->sni_cowed_dentry); + //d_unalloc(snap_info->sni_cowed_dentry); + if (snap_table) { + table_size = SNAPTABLE_SIZE(snap_table->sntbl_max_count); + OBD_FREE(snap_info->sni_table, table_size); + } + smfs_cleanup_dotinfo(snap_info); + RETURN(rc); +} - rc = snapops->fs_get_snap_info(NULL, inode, SNAP_GENERATION, - strlen(SNAP_GENERATION), - &sni_info->sn_gen, &vallen); - } - RETURN(rc); - +int smfs_snap_test_inode(struct inode *inode, void *args) +{ + struct smfs_iget_args *sargs = (struct smfs_iget_args*)args; + struct inode *dir; + + LASSERT(sargs); + + dir = sargs->s_inode; + + if (sargs->s_index > 0) { + if (I2SNAPI(inode)->sn_index != sargs->s_index) + return 0; + }else { + if (dir && I2SNAPI(inode)->sn_index != I2SNAPI(dir)->sn_index) + return 0; + } + return 1; } /* latest snap: returns - the index of the latest snapshot before NOW - hence it returns 0 in case all the volume snapshots lie in the future - this is the index where a COW will land (will be created) */ -void snap_last(struct super_block *sb, struct snap *snap) +void snap_last(struct inode *inode, struct snap *snap) { - struct snap_info *snap_info = S2SNAPI(sb); - struct snap_table *table = snap_info->sntbl; - time_t now = CURRENT_TIME; + time_t now = LTIME_S(CURRENT_TIME); + struct snap_table *snap_table; + struct snap_info *snap_info; int i ; ENTRY; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for inode %p\n", inode); + EXIT; + return; + } + snap_table = snap_info->sni_table; /* start at the highest index in the superblock snaptime array */ - if (table->sntbl_count == 0) { + if (snap_table->sntbl_count == 0) { memset(snap, 0, sizeof(struct snap)); } else { - i = table->sntbl_count - 1; - snap->sn_index = table->sntbl_items[i].sn_index; - snap->sn_time = table->sntbl_items[i].sn_time; - snap->sn_gen = table->sntbl_items[i].sn_gen; + i = snap_table->sntbl_count - 1; + snap->sn_index = snap_table->sntbl_items[i].sn_index; + snap->sn_time = snap_table->sntbl_items[i].sn_time; + snap->sn_gen = snap_table->sntbl_items[i].sn_gen; } CDEBUG(D_INFO, "index: %d, time[i]: %ld, now: %ld\n", snap->sn_index, snap->sn_time, now); @@ -218,7 +609,7 @@ void snap_last(struct super_block *sb, struct snap *snap) return; } -static int inline get_index_of_item(struct snap_table *table, char *name) +static inline int get_index_of_item(struct snap_table *table, char *name) { int count = table->sntbl_count; int i, j; @@ -231,7 +622,7 @@ static int inline get_index_of_item(struct snap_table *table, char *name) } } - for (i = 0; i < table->sntbl_max_count; i++) { + for (i = 1; i <= table->sntbl_max_count; i++) { int found = 0; for (j = 0; j < (count + 1); j++) { if (table->sntbl_items[j].sn_index == i) { @@ -246,21 +637,53 @@ static int inline get_index_of_item(struct snap_table *table, char *name) RETURN(-ENOSPC); } -int smfs_add_snap_item(struct super_block *sb, char *name) +static struct dentry *smfs_find_snap_root(struct super_block *sb, + char *path_name) { - struct snap_info *snap_info = S2SNAPI(sb); - struct fsfilt_operations *snapops = snap_info->snap_fsfilt; - struct snap_table *snap_table = snap_info->sntbl; - struct snap *snap_item; - int table_size, count = 0, index = 0, rc = 0; + struct dentry *dentry = NULL; + struct nameidata nd; + ENTRY; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) + if (path_init(path_name, LOOKUP_FOLLOW, &nd)) { + int error = path_walk(path_name, &nd); + if (error) { + path_release(&nd); + RETURN(NULL); + } + } else { + RETURN(NULL); + } +#else + if (path_lookup(path_name, LOOKUP_FOLLOW, &nd)) + RETURN(NULL); +#endif + dentry = dget(nd.dentry); + path_release(&nd); + RETURN(dentry); +} + +static int snap_add_item(struct smfs_super_info *smb, + struct snap_info *snap_info, + char *name) +{ + struct fsfilt_operations *snapops; + struct snap_table *snap_table = snap_info->sni_table; + struct inode *root_inode = NULL; + int table_size, count = 0, index = 0, rc = 0; + struct snap *snap_item; + ENTRY; count = snap_table->sntbl_count; + root_inode = iget(smb->smsi_sb, snap_info->sni_root_ino); + if (!root_inode || is_bad_inode(root_inode)) + RETURN(-EIO); /* XXX Is down this sema necessary*/ - down_interruptible(&snap_info->sntbl_sema); + down_interruptible(&snap_info->sni_sema); snap_item = &snap_table->sntbl_items[count]; - + snapops = smb->smsi_snap_info->snap_cache_fsfilt; /*add item in snap_table set generation*/ - snap_item->sn_time = CURRENT_TIME; + snap_item->sn_time = LTIME_S(CURRENT_TIME); /* find table index */ index = get_index_of_item(snap_table, name); if (index < 0) @@ -272,21 +695,127 @@ int smfs_add_snap_item(struct super_block *sb, char *name) memcpy(snap_item->sn_name, name, SNAP_MAX_NAMELEN); /* Wrote the whole snap_table to disk */ table_size = SNAPTABLE_SIZE(snap_table->sntbl_max_count); - - rc = snapops->fs_set_snap_info(sb, NULL, SNAPTABLE_INFO, + + snap_table->sntbl_count++; + snap_table->sntbl_generation++; + rc = snapops->fs_set_snap_info(root_inode, SNAPTABLE_INFO, strlen(SNAPTABLE_INFO), snap_table, &table_size); if (rc) { + snap_table->sntbl_count--; + snap_table->sntbl_generation--; CERROR("Set snaptable error rc=%d\n", rc); GOTO(exit, rc); } - snap_table->sntbl_count++; - snap_table->sntbl_generation++; exit: - up(&snap_info->sntbl_sema); + up(&snap_info->sni_sema); + if (root_inode) + iput(root_inode); RETURN(rc); } -EXPORT_SYMBOL(smfs_add_snap_item); + +static struct snap_info * smfs_find_create_snap_info(struct super_block *sb, + struct inode *inode) +{ + struct snap_super_info *snap_sinfo = S2SNAPI(sb); + struct fsfilt_operations *sops = snap_sinfo->snap_cache_fsfilt; + struct snap_info *snap_info, *tmp; + ino_t *snap_root = NULL; + int rino_size, snap_count_size, rc = 0; + ENTRY; + + list_for_each_entry_safe(snap_info, tmp, &snap_sinfo->snap_list, + sni_list) { + if (snap_info->sni_root_ino == inode->i_ino) { + RETURN(snap_info); + } + } + + CDEBUG(D_INFO, "create a new snap info root ino %lu\n", inode->i_ino); + + snap_info = smfs_create_snap_info(S2SMI(sb), inode); + + if (IS_ERR(snap_info)) + RETURN(snap_info); + + snap_sinfo->snap_count++; + + rino_size = snap_sinfo->snap_count * sizeof(ino_t); + + OBD_ALLOC(snap_root, rino_size); + + if (!snap_root) + GOTO(exit, rc = -ENOMEM); + + rc = sops->fs_get_snap_info(I2CI(inode), SNAP_ROOT_INO, + strlen(SNAP_ROOT_INO), snap_root, + &rino_size); + if (rc < 0) { + if (rc == -ENODATA) { + rc = 0; + } else { + GOTO(exit, rc); + } + } + snap_root[snap_sinfo->snap_count - 1] = inode->i_ino; + + snap_count_size = sizeof(int); + rc = sops->fs_set_snap_info(I2CI(inode), SNAP_COUNT, strlen(SNAP_COUNT), + &snap_sinfo->snap_count, &snap_count_size); + if (rc) + GOTO(exit, rc); + + rc = sops->fs_set_snap_info(I2CI(inode), SNAP_ROOT_INO, + strlen(SNAP_ROOT_INO), snap_root, + &rino_size); + + if (rc) + GOTO(exit, rc); + + list_add(&snap_info->sni_list, &snap_sinfo->snap_list); +exit: + if (rc) { + smfs_cleanup_snap_info(snap_info); + OBD_FREE(snap_info, sizeof(struct snap_info)); + } + if (snap_root) + OBD_FREE(snap_root, rino_size); + RETURN(snap_info); +} + +int smfs_add_snap_item(struct super_block *sb, char *path_name, char *name) +{ + struct dentry *dentry = NULL; + struct snap_info *snap_info; + int rc = 0; + ENTRY; + + if (!SMFS_DO_COW(S2SMI(sb))) { + RETURN(0); + } + + if (!path_name || !name) { + CERROR("patch_name and snapshot_name is NULL"); + RETURN(-EINVAL); + } + dentry = smfs_find_snap_root(sb, path_name); + if (IS_ERR(dentry)) { + CERROR("can not find snap_shot root by %s\n", path_name); + RETURN(PTR_ERR(dentry)); + } + snap_info = smfs_find_create_snap_info(sb, dentry->d_inode); + if (IS_ERR(snap_info)) { + CERROR("can not find snap_info by %s rc=%lu\n", path_name, + PTR_ERR(snap_info)); + GOTO(exit, rc = PTR_ERR(snap_info)); + } + + rc = snap_add_item(S2SMI(sb), snap_info, name); +exit: + dput(dentry); + RETURN(rc); +} +//EXPORT_SYMBOL(smfs_add_snap_item); /* * Note: this function should be differnet with snap_do_cow. * In smfs_do_cow, we check the EA for whether do cow for that inode. @@ -302,7 +831,7 @@ int smfs_needs_cow(struct inode *inode) snap_info = &(smi_info->sm_sninfo); - snap_last(inode->i_sb, &snap); + snap_last(inode, &snap); /* decision .... if the snapshot is more recent than the object, * then any change to the object should cause a COW. */ @@ -315,42 +844,126 @@ int smfs_needs_cow(struct inode *inode) RETURN(index); } /* snap_needs_cow */ +static int link_cowed_inode(struct inode *inode) +{ + struct dentry *cowed_dir = NULL; + char idname[LL_ID_NAMELEN]; + struct snap_info *snap_info; + int idlen = 0, rc = 0; + struct dentry *dchild = NULL; + struct dentry *tmp = NULL; + unsigned mode; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + CERROR("can not find snap info for inode %p\n", inode); + RETURN(-EINVAL); + } + + cowed_dir = snap_info->sni_cowed_dentry; + + down(&cowed_dir->d_inode->i_sem); + + idlen = ll_id2str(idname, inode->i_ino, inode->i_generation); + dchild = lookup_one_len(idname, cowed_dir, idlen); + if (IS_ERR(dchild)) { + rc = PTR_ERR(dchild); + if (rc != -EPERM && rc != -EACCES) + CERROR("child lookup error %d\n", rc); + GOTO(out_lock, rc); + } + if (dchild->d_inode != NULL) { + CERROR("re-cowed file %s?\n", dchild->d_name.name); + LASSERT(dchild->d_inode == inode); + GOTO(out_dput, rc = 0); + } + tmp = pre_smfs_dentry(NULL, inode, cowed_dir); + /* link() is semanticaly-wrong for S_IFDIR, so we set S_IFREG + * for linking and return real mode back then -bzzz */ + mode = inode->i_mode; + inode->i_mode = S_IFREG; + + rc = cowed_dir->d_inode->i_op->link(tmp, cowed_dir->d_inode, dchild); + + post_smfs_dentry(tmp); + if (rc) { + CERROR("error linking cowed inode %s to COWED: rc = %d\n", + idname, rc); + } + inode->i_mode = mode; + if ((mode & S_IFMT) == S_IFDIR) { + dchild->d_inode->i_nlink++; + cowed_dir->d_inode->i_nlink++; + mark_inode_dirty(cowed_dir->d_inode); + mark_inode_dirty(dchild->d_inode); + } +out_dput: + up(&cowed_dir->d_inode->i_sem); + dput(dchild); +out_lock: + RETURN(rc); +} /* * Make a copy of the data and plug a redirector in between if there * is no redirector yet. */ int snap_do_cow(struct inode *inode, struct dentry *dparent, int del) { - struct snap_info *snap_info = S2SNAPI(inode->i_sb); - struct fsfilt_operations *snapops = snap_info->snap_fsfilt; + struct fsfilt_operations *snapops = I2SNAPCOPS(inode); struct snap snap; - struct inode *ind = NULL; - - ENTRY; + struct inode *cache_ind = NULL; + ENTRY; if (!snapops || !snapops->fs_create_indirect) RETURN(-EINVAL); - snap_last(inode->i_sb, &snap); - ind = snapops->fs_create_indirect(inode, snap.sn_index, snap.sn_gen, - dparent->d_inode, del); - if(!ind) - RETURN(-EINVAL); - - I2SMI(ind)->sm_sninfo.sn_flags = 0; - I2SMI(ind)->sm_sninfo.sn_gen = snap.sn_gen; - - iput(ind); + snap_last(inode, &snap); + cache_ind = snapops->fs_create_indirect(I2CI(inode), snap.sn_index, + snap.sn_gen, I2CI(dparent->d_inode), + del); + if(cache_ind && IS_ERR(cache_ind)) { + CERROR("Create ind inode %lu index %d gen %d del %d rc%lu\n", + inode->i_ino, snap.sn_index, snap.sn_gen, del, + PTR_ERR(cache_ind)); + RETURN(PTR_ERR(cache_ind)); + } + if (cache_ind) { + iput(cache_ind); + if (!SMFS_DO_INODE_COWED(inode)) { + /*insert the inode to cowed inode*/ + SMFS_SET_INODE_COWED(inode); + link_cowed_inode(inode); + } + } RETURN(0); } - -int smfs_cow_create(struct inode *dir, struct dentry *dentry) +/*Dir inode will do cow*/ +int smfs_cow_create_pre(struct inode *dir, void *de, void *data1, void *data2) { + struct dentry *dparent; + struct dentry *dentry = (struct dentry *)de; int rc = 0; ENTRY; 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 ((rc = snap_do_cow(dir, dparent, 0))) { + CERROR("Do cow error %d\n", rc); + RETURN(-EINVAL); + } + } + RETURN(rc); +} + +int smfs_cow_setattr_pre(struct inode *dir, void *de, void *data1, void *data2) +{ + struct dentry *dentry = (struct dentry *)de; + int rc = 0; + ENTRY; + if (smfs_needs_cow(dir) != -1) { + CDEBUG(D_INODE, "snap_needs_cow for ino %lu \n",dir->i_ino); if ((snap_do_cow(dir, dentry->d_parent, 0))) { CERROR("Do cow error\n"); RETURN(-EINVAL); @@ -359,59 +972,564 @@ 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_link_pre(struct inode *dir, void *de, void *data1, void *data2) { + struct dentry *dparent; + struct dentry *dentry = (struct dentry *)de; int rc = 0; ENTRY; - + + 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); + } + } RETURN(rc); } -int smfs_cow_link(struct inode *dir, struct dentry *dentry) +int smfs_cow_unlink_pre(struct inode *dir, void *de, void *data1, void *data2) { + struct dentry *dentry = (struct dentry *)de; + struct dentry *dparent; int rc = 0; ENTRY; + + 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, 1))) { + CERROR("Do cow error\n"); + RETURN(-EINVAL); + } + } RETURN(rc); } -int smfs_cow_unlink(struct inode *dir, struct dentry *dentry) +int smfs_cow_rename_pre(struct inode *dir, void *de, void *data1, void *data2) { + struct dentry *dentry = (struct dentry*)de; + 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_rename(struct inode *dir, struct dentry *dentry) +int smfs_cow_write_pre(struct inode *inode, void *de, void *data1, void *data2) { - int rc = 0; + struct dentry *dentry = (struct dentry*)de; + struct snap_info *snap_info = NULL; + struct snap_table *table; + long blocks[2]={-1,-1}; + int index = 0, i, rc = 0; + size_t count; + loff_t pos; + ENTRY; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for inode %p\n", inode); + RETURN(0); + } + table = snap_info->sni_table; + + LASSERT(data1); + LASSERT(data2); + + count = *(size_t *)data1; + pos = *(loff_t*)data2; + + 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 *sops = I2SNAPCOPS(inode); + struct inode *cind = NULL; + int result = 0; + + index = table->sntbl_items[slot].sn_index; + cind = sops->fs_get_indirect(I2CI(inode), NULL, index); + if (!cind) continue; + + CDEBUG(D_INFO, "find cache_ino %lu\n", cind->i_ino); + + result = sops->fs_copy_block(I2CI(inode), cind, + blocks[i]); + if (result == 1) { + iput(cind); + result = 0; + break; + } + if (result < 0) { + iput(cind); + up(&inode->i_sem); + GOTO(exit, rc = result); + } + iput(cind); + } + } +exit: + up(&inode->i_sem); RETURN(rc); } +EXPORT_SYMBOL(smfs_cow_write_pre); +/*lookup inode in dotsnap inode */ +static int smfs_dotsnap_lookup(struct inode *dir, struct dentry *dentry, + struct snap_info *snap_info) +{ + if (dentry->d_name.len == 1 && + !strcmp(dentry->d_name.name, ".")) { + d_add(dentry, iget(dir->i_sb, dir->i_ino)); + } else if (dentry->d_name.len == 2 && + !strcmp(dentry->d_name.name, "..")) { + struct inode *inode; + struct dentry *dparent = dentry->d_parent; + if (dparent->d_inode) { + inode = iget(dir->i_sb, dparent->d_inode->i_ino); + if (inode) { + if (!is_bad_inode(inode)) + d_add(dentry, inode); + else + iput(inode); + } + } + } else { + /*find the name from the snaptable*/ + struct fsfilt_operations *sops = I2SNAPCOPS(dir); + struct snap_table *table; + struct inode *inode; + ino_t cino; + int i = 0, index = -1; -int smfs_cow_write(struct inode *dir, struct dentry *dentry) + table = snap_info->sni_table; + + for (i = 0; i < table->sntbl_count; i++) { + char *name = table->sntbl_items[i].sn_name; + if ((dentry->d_name.len == strlen(name)) && + (memcmp(dentry->d_name.name, name, + dentry->d_name.len) == 0)) { + index = table->sntbl_items[i].sn_index; + break; + } + } + if (index == -1) { + CERROR("No such %s in this .snap dir \n", + dentry->d_name.name); + RETURN(-ENOENT); + } + cino = sops->fs_get_indirect_ino(S2CSB(dir->i_sb), dir->i_ino, + index); + if (cino == 0) + cino = dir->i_ino; + inode = smfs_get_inode(dir->i_sb, cino, dir, index); + if (!inode || is_bad_inode(inode)) { + CERROR("Can not find cino %lu inode\n", cino); + RETURN(-ENOENT); + } + smfs_init_snap_inode_info(inode, dir, index); + d_add(dentry, inode); + } + RETURN(0); +} +int smfs_cow_lookup_pre(struct inode *inode, void *de, void *data1, + void *data2) { + struct dentry *dentry = (struct dentry*)de; + struct snap_info *snap_info; + struct snap_dot_info *dot_info; int rc = 0; ENTRY; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for inode %p\n", inode); + RETURN(0); + } - RETURN(rc); + dot_info = snap_info->sni_dot_info; + + if (smfs_primary_inode(inode) && + dentry->d_name.len == dot_info->dot_name_len && + memcmp(dentry->d_name.name, dot_info->dot_name, + strlen(dot_info->dot_name)) == 0) { + struct inode *dot_inode = NULL; + + dot_inode = smfs_get_inode(inode->i_sb, inode->i_ino, inode, + DOT_SNAP_INDEX); + smfs_init_snap_inode_info(dot_inode, inode, DOT_SNAP_INDEX); + d_add(dentry, dot_inode); + rc = 1; + RETURN(rc); + } else if (smfs_dotsnap_inode(inode)) { + rc = smfs_dotsnap_lookup(inode, dentry, snap_info); + if (rc == 0) + rc = 1; + RETURN(rc); + } else { + /*HERE: will replace ino in dentry->d_name according to index, + *For iopen, will fix it in integrating snapfs to Lustre*/ +#if 0 + struct fsfilt_operations *snapops = I2SNAPOPS(inode); + char *name = (char *)dentry->d_name.name; + unsigned long ino, hash, ind_ino; + int len = sizeof(ind_ino); + + ino = simple_strtoul(name, 0, 0); + + ind_ino = snapops->fs_get_indirect_ino(inode->i_sb, ino, index); + + snprintf(name, strlen(name), "0x%lx", ind_ino); + + hash = init_name_hash(); + while (len--) { + unsigned char c; + c = *(const unsigned char *)name++; + if (c == '\0') break; + hash = partial_name_hash(c, hash); + } + dentry->d_name.hash = end_name_hash(hash); +#endif + } + RETURN(rc); +} + +struct inode *smfs_cow_get_ind(struct inode *inode, int index) +{ + long block=(index << PAGE_CACHE_SHIFT) >> inode->i_sb->s_blocksize_bits; + struct fsfilt_operations *sops = I2SNAPCOPS(inode); + struct snap_info *snap_info = NULL; + struct snap_table *table = NULL; + int slot; + + ENTRY; + + snap_info = smfs_find_snap_info(inode); + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for inode %p\n", inode); + RETURN(NULL); + } + + table = snap_info->sni_table; + + for (slot = table->sntbl_count - 1; slot >= 0; slot--) { + struct address_space_operations *aops = inode->i_mapping->a_ops; + struct inode *cache_inode = NULL; + int index = 0; + + index = table->sntbl_items[slot].sn_index; + cache_inode = sops->fs_get_indirect(I2CI(inode), NULL, index); + + if (!cache_inode ) continue; + + if (aops->bmap(cache_inode->i_mapping, block)) + RETURN(cache_inode); + iput(cache_inode); + } + + RETURN(NULL); } +EXPORT_SYMBOL(smfs_cow_get_ind); -typedef int (*cow_funcs)(struct inode *dir, struct dentry *dentry); +static int smfs_cow_readdir_pre(struct inode *dir, void *de, void *data1, + void *data2) +{ + struct file *filp = (struct file*)de; + void *dirent = data1; + filldir_t filldir = (filldir_t)data2; + struct snap_info *snap_info = NULL; + + if (smfs_under_dotsnap_inode(dir)) + RETURN(0); + + snap_info = smfs_find_snap_info(dir); + + if (!snap_info) { + CDEBUG(D_INFO, "can not find snap info for ino %lu\n", + dir->i_ino); + RETURN(-EINVAL); + } -static cow_funcs smfs_cow_funcs[REINT_MAX + 1] = { - [REINT_SETATTR] smfs_cow_setattr, - [REINT_CREATE] smfs_cow_create, - [REINT_LINK] smfs_cow_link, - [REINT_UNLINK] smfs_cow_unlink, - [REINT_RENAME] smfs_cow_rename, - [REINT_WRITE] smfs_cow_write, + if (smfs_primary_inode(dir)) { + if (filp->f_pos == 0) { + struct snap_dot_info *dot = snap_info->sni_dot_info; + if (filldir(dirent, dot->dot_name, dot->dot_name_len, + filp->f_pos, -1, 0)) { + CERROR("fill .snap error \n"); + RETURN(-EINVAL); + } + } else { + filp->f_pos -= 1; + } + } else if (smfs_dotsnap_inode(dir)) { + struct snap_table *table = snap_info->sni_table; + int i = 0; + + if (filp->f_pos < 0) + RETURN(-EINVAL); + + if ((filp->f_pos == 0) && filldir(dirent, ".", 1, + filp->f_pos++, + dir->i_ino, 0) < 0) + RETURN(-EIO); + if ((filp->f_pos == 1) && filldir(dirent, "..", 2, + filp->f_pos++, + dir->i_ino, 0) < 0) + RETURN(-EIO); + + for (i = filp->f_pos - 2; i < table->sntbl_count; i++, + filp->f_pos++) { + int slot = table->sntbl_count - i - 1; + + if (filldir(dirent, table->sntbl_items[slot].sn_name, + strlen(table->sntbl_items[slot].sn_name), + filp->f_pos, dir->i_ino, 0)) + break; + + } + RETURN(1); + } + + RETURN(0); +} + + +typedef int (*cow_funcs)(struct inode *dir, void *dentry, void *new_dir, + void *new_dentry); + +static cow_funcs smfs_cow_pre_funcs[HOOK_MAX + 1] = { + [HOOK_CREATE] smfs_cow_create_pre, + [HOOK_LOOKUP] smfs_cow_lookup_pre, + [HOOK_LINK] smfs_cow_link_pre, + [HOOK_UNLINK] smfs_cow_unlink_pre, + [HOOK_SYMLINK] smfs_cow_create_pre, + [HOOK_MKDIR] smfs_cow_create_pre, + [HOOK_RMDIR] smfs_cow_unlink_pre, + [HOOK_MKNOD] smfs_cow_create_pre, + [HOOK_RENAME] smfs_cow_rename_pre, + [HOOK_SETATTR] smfs_cow_setattr_pre, + [HOOK_WRITE] smfs_cow_write_pre, + [HOOK_READDIR] smfs_cow_readdir_pre, }; -int smfs_cow(struct inode *dir, struct dentry *dentry, int op) +static int smfs_revalidate_dotsnap_dentry(struct dentry *dentry, + struct inode *dir, int index) { - return smfs_cow_funcs[op](dir, dentry); + struct inode *inode = dentry->d_inode; + ENTRY; + + if (!inode) + RETURN(0); + + if (index > 0 && index != DOT_SNAP_INDEX) { + struct fsfilt_operations *sops = I2SNAPCOPS(inode); + struct inode *cache_ind = NULL; + + cache_ind = sops->fs_get_indirect(I2CI(inode), NULL, index); + + if (cache_ind) { + struct inode *ind_inode = NULL; + + LASSERT(cache_ind->i_ino != I2CI(inode)->i_ino); + + ind_inode = smfs_get_inode(inode->i_sb, cache_ind->i_ino, + dir, index); + list_del_init(&dentry->d_alias); + iput(inode); + d_instantiate(dentry, ind_inode); + iput(cache_ind); + } + } + RETURN(0); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int +smfs_revalidate_nd(struct dentry *de, struct nameidata *nd) +{ + struct inode *inode = de->d_inode; + ENTRY; + + if (!inode) + RETURN(0); + + if (smfs_under_dotsnap_inode(inode)) { + struct inode *dir = de->d_parent->d_inode; + int index = I2SNAPI(inode)->sn_index; + + smfs_revalidate_dotsnap_dentry(de, dir, index); + smfs_init_snap_inode_info(de->d_inode, dir, index); + } + + RETURN(0); +} +#else +static int +smfs_revalidate_it(struct dentry *de, int flags, + struct nameidata *nd, + struct lookup_intent *it) +{ + struct inode *inode = de->d_inode; + ENTRY; + + if (!inode) + RETURN(0); + + if (smfs_under_dotsnap_inode(inode)) { + struct inode *dir = de->d_parent->d_inode; + int index = I2SNAPI(inode)->sn_index; + + smfs_revalidate_dotsnap_dentry(de, dir, index); + smfs_init_snap_inode_info(de->d_inode, dir, index); + } + + RETURN(0); +} +#endif + +static int smfs_delete_dentry(struct dentry *dentry) +{ + dentry->d_op = NULL; + return 0; +} + +struct dentry_operations smfs_cow_dops = { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + .d_revalidate = smfs_revalidate_nd, +#else + .d_revalidate_it = smfs_revalidate_it, +#endif + .d_delete = smfs_delete_dentry, +}; + +int smfs_cow_lookup_post(struct inode *dir, void *de, void *data1, + void *data2) +{ + struct dentry *dentry = (struct dentry*)de; + struct inode *inode = dentry->d_inode; + ENTRY; + + if (inode && smfs_under_dotsnap_inode(inode)) { + int index = I2SNAPI(dir)->sn_index; + + smfs_revalidate_dotsnap_dentry(dentry, dir, index); + smfs_init_snap_inode_info(inode, dir, index); + } + dentry->d_op = &smfs_cow_dops; + RETURN(0); +} + +static int smfs_cow_readdir_post(struct inode *dir, void *de, void *data1, + void *data2) +{ + struct file *filp = (struct file*)de; + + if (smfs_primary_inode(dir)) { + filp->f_pos += 1; + } + RETURN(0); +} + + +static cow_funcs smfs_cow_post_funcs[HOOK_MAX + 1] = { + [HOOK_CREATE] NULL, + [HOOK_LOOKUP] smfs_cow_lookup_post, + [HOOK_LINK] NULL, + [HOOK_UNLINK] NULL, + [HOOK_SYMLINK] NULL, + [HOOK_MKDIR] NULL, + [HOOK_RMDIR] NULL, + [HOOK_MKNOD] NULL, + [HOOK_RENAME] NULL, + [HOOK_SETATTR] NULL, + [HOOK_WRITE] NULL, + [HOOK_READDIR] smfs_cow_readdir_post, +}; + +static int smfs_cow_pre(struct inode *dir, void *dentry, void *new_dir, + void *new_dentry, int op) +{ + if (smfs_cow_pre_funcs[op]) { + return smfs_cow_pre_funcs[op](dir, dentry, new_dir, new_dentry); + } + return 0; +} + +static int smfs_cow_post(struct inode *dir, void *dentry, void *new_dir, + void *new_dentry, int op) +{ + if (smfs_cow_post_funcs[op]) { + return smfs_cow_post_funcs[op](dir, dentry, new_dir, new_dentry); + } + return 0; }