/* * snaptable.c * * Manipulate snapshot tables * */ #define DEBUG_SUBSYSTEM S_SNAP #include #include #include #include #include #include #include #include #include #include "snapfs_internal.h" struct snap_table snap_tables[SNAP_MAX_TABLES]; int snap_index2slot(struct snap_table *snap_table, int snap_index) { int slot; for ( slot=0 ; slot < snap_table->tbl_count ; slot++ ) if ( snap_table->snap_items[slot].index == snap_index ) return slot; 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 snap_cache *info, struct snap *snap) { int i ; time_t now = CURRENT_TIME; struct snap_table *table; int tableno = info->cache_snap_tableno; ENTRY; if ( tableno < 0 || tableno > SNAP_MAX_TABLES ) { CERROR("invalid table no %d\n", tableno); snap->index = -1; } table = &snap_tables[tableno]; /* start at the highest index in the superblock snaptime array */ i = table->tbl_count - 1; /* NOTE: i>0 is an unnecessary check */ snap->index = table->snap_items[i].index; snap->time = table->snap_items[i].time; snap->gen = table->snap_items[i].gen; CDEBUG(D_SNAP, "index: %d, time[i]: %ld, now: %ld\n", snap->index, snap->time, now); return; } /* return -1 if no COW is needed, otherwise the index of the clone to COW to is returned */ int snap_needs_cow(struct inode *inode) { struct snap snap; struct snap_cache *cache; struct filter_inode_info *filter_info; int index = -1; ENTRY; cache = snap_find_cache(inode->i_dev); if ( !cache ) { RETURN(index); } filter_info = (struct filter_inode_info *) inode->i_filterdata; /* here we find the time of the last snap to compare with */ snap_last(cache, &snap); /* decision .... if the snapshot is more recent than the object, * then any change to the object should cause a COW. */ if (filter_info && filter_info->generation < snap.gen ) { index = snap.index; } CDEBUG(D_SNAP, "snap_needs_cow, ino %lu , get index %d\n", inode->i_ino, index); RETURN(index); } /* snap_needs_cow */ int snap_print_table(struct ioc_snap_tbl_data *data, char *buf, int *buflen) { struct snap_table *table; struct ioc_snap_tbl_data *stbl_out; int tableno = data->no; int i, rc = 0, nleft = (*buflen); char *buf_ptr; if (tableno < 0 || tableno > SNAP_MAX_TABLES) { CERROR("invalid table number %d\n", tableno); RETURN(-EINVAL); } table = &snap_tables[tableno]; stbl_out = (struct ioc_snap_tbl_data *)buf; stbl_out->count = table->tbl_count - 1; stbl_out->no = tableno; buf_ptr = (char*)stbl_out->snaps; nleft -= buf_ptr - buf; for (i = 1; i < table->tbl_count; i++) { memcpy(buf_ptr, &table->snap_items[i], sizeof(struct snap)); nleft -= sizeof(struct snap); if(nleft < 0) { CERROR("can not get enough space to print snaptable\n"); rc = -ERANGE; goto exit; } else { buf_ptr += sizeof(struct snap); } } exit: if(nleft > 0) (*buflen) = (*buflen) - nleft; return 0; } static int inline get_index_of_item(struct snap_table *table, char *name) { int count = table->tbl_count; int i, j; for (i = 0; i < SNAP_MAX; i++) { if (!strcmp(name, table->snap_items[i].name)) return -EINVAL; } for (i = 0; i < SNAP_MAX; i++) { int found = 0; for (j = 0; j < (count + 1); j++) { if (table->snap_items[j].index == i) { found = 1; break; } } if (!found) return i; } return -EINVAL; } /* This function will write one item(a snapshot) to snaptable * and will also write to disk. */ static int snaptable_add_item(struct ioc_snap_tbl_data *data) { struct snap_table *table; struct snap_disk_table *disk_snap_table; struct snapshot_operations *snapops; struct snap_cache *cache; int tableno , index, i, count, rc; if (!(cache = snap_find_cache((kdev_t)data->dev))) RETURN(-ENODEV); snapops = filter_c2csnapops(cache->cache_filter); if (!snapops || !snapops->set_meta_attr) RETURN(-EINVAL); tableno = data->no; if (tableno < 0 || tableno > SNAP_MAX_TABLES) { CERROR("invalid table number %d\n", tableno); RETURN(-EINVAL); } table = &snap_tables[tableno]; count = table->tbl_count; /* XXX Is down this sema necessary*/ down_interruptible(&table->tbl_sema); /*add item in snap_table set generation*/ table->snap_items[count].gen = table->generation + 1; table->snap_items[count].time = CURRENT_TIME; /* find table index */ index = get_index_of_item(table, data->snaps[0].name); if (index < 0) { CERROR("snaptable full Or Duplicate name in snaptable\n"); GOTO(exit, rc = -EINVAL); } table->snap_items[count].index = index; table->snap_items[count].flags = 0; memcpy(&table->snap_items[count].name[0], data->snaps[0].name, SNAP_MAX_NAMELEN); /* we will write the whole snap_table to disk */ SNAP_ALLOC(disk_snap_table, sizeof(struct snap_disk_table)); if (!disk_snap_table) GOTO(exit, rc = -ENOMEM); disk_snap_table->magic = cpu_to_le32((__u32)DISK_SNAP_TABLE_MAGIC); disk_snap_table->count = cpu_to_le32((__u32)table->tbl_count); disk_snap_table->generation = cpu_to_le32((__u32)table->generation + 1); memset(&disk_snap_table->snap_items[0], 0, SNAP_MAX * sizeof(struct snap_disk)); for (i = 1; i <= count; i++) { struct snap *item = &table->snap_items[i]; disk_snap_table->snap_items[i-1].time = cpu_to_le64((__u64)item->time); disk_snap_table->snap_items[i-1].gen = cpu_to_le32((__u32)item->gen); disk_snap_table->snap_items[i-1].flags = cpu_to_le32((__u32)item->flags); disk_snap_table->snap_items[i-1].index = cpu_to_le32((__u32)item->index); memcpy(&disk_snap_table->snap_items[i-1].name , item->name, SNAP_MAX_NAMELEN); } rc = snapops->set_meta_attr(cache->cache_sb, DISK_SNAPTABLE_ATTR, (char*)disk_snap_table, sizeof(struct snap_disk_table)); SNAP_FREE(disk_snap_table, sizeof(struct snap_disk_table)); table->tbl_count++; table->generation++; exit: up(&table->tbl_sema); RETURN(rc); } static int delete_inode(struct inode *primary, void *param) { struct snap_iterdata * data; int tableno = 0; int index = 0; int rc = 0; struct inode *redirect; ino_t old_ind = 0; struct snap_table *table; int slot; int delete_slot; int this_index; struct inode *next_ind = NULL; int my_table[SNAP_MAX]; ENTRY; if(!primary) RETURN(0); data = (struct snap_iterdata*) param; if (data) { index = data->index; tableno = data->tableno; } CDEBUG(D_SNAP, "delete_inode ino %lu, index %d\n", primary->i_ino, index); table = &snap_tables[tableno]; redirect = snap_get_indirect(primary, NULL, index); if (!redirect) { CDEBUG(D_SNAP, "redirect inode index %d not exist \n", index); RETURN(0); } old_ind = redirect->i_ino; iput(redirect); /* In destroy indirect inode, we lock the primary inode here */ down(&primary->i_sem); slot = snap_index2slot(table, index) - 1; if (slot > 0) { this_index = table->snap_items[slot].index; redirect = snap_get_indirect(primary, NULL, this_index); if (redirect) { iput(redirect); } else { snap_set_indirect(primary, old_ind, this_index, 0); snap_set_indirect(primary, 0, index, 0); up(&primary->i_sem); RETURN(0); } } delete_slot = snap_index2slot(table, index); for (slot = table->tbl_count - 1; slot > delete_slot; slot --) { my_table[slot - delete_slot] = table->snap_items[slot].index; } this_index = table->tbl_count - delete_slot - 1; next_ind = snap_get_indirect(primary, my_table, this_index); if (next_ind && (next_ind->i_ino == primary->i_ino)) { iput(next_ind); next_ind = NULL; } rc = snap_destroy_indirect(primary, index, next_ind); up(&primary->i_sem); if (next_ind) iput(next_ind); if (rc != 0) CERROR("snap_destroy_indirect(ino %lu,index %d),ret %d\n", primary->i_ino, index, rc); RETURN(0); } static int snap_delete(struct super_block *sb, struct snap_iterdata *data) { CDEBUG(D_SNAP, "dev %d, tableno %d, index %d, time %lu\n", data->dev, data->tableno, data->index, data->time ); snap_iterate(sb, &delete_inode, NULL, data, SNAP_ITERATE_COWED_INODE); return 0; } /* This function will delete one item(a snapshot) in the snaptable * and will also delete the item in the disk. * FIXME later, this should be in a transaction. */ int snaptable_delete_item(struct super_block *sb, struct snap_iterdata *data) { struct snap_table *table; struct snap_disk_table *disk_snap_table; struct snapshot_operations *snapops; struct snap_cache *cache; int tableno = data->tableno; int index, i, del_slot, rc; if (!(cache = snap_find_cache((kdev_t)data->dev))) RETURN(-ENODEV); if (tableno < 0 || tableno > SNAP_MAX_TABLES) { CERROR("invalid table number %d\n", tableno); RETURN(-EINVAL); } snapops = filter_c2csnapops(cache->cache_filter); if (!snapops || !snapops->set_meta_attr) RETURN(-EINVAL); index = data->index; if (clonefs_mounted(cache, index)) { CERROR("Please first umount this clonefs \n"); RETURN(-EBUSY); } /*first delete the snapshot * FIXME if snap delete error, how to handle this error*/ rc = snap_delete(sb, data); if (rc) RETURN(-EINVAL); /*delete item in snaptable */ table = &snap_tables[tableno]; del_slot = snap_index2slot(table, index); if (del_slot < 0) RETURN(-EINVAL); down_interruptible(&table->tbl_sema); SNAP_ALLOC(disk_snap_table, sizeof(struct snap_disk_table)); if (!disk_snap_table) { up(&table->tbl_sema); RETURN(-ENOMEM); } /* we will delete the item snap_table to disk */ index = del_slot; /*Move the items after the delete slot forward one step*/ memset(&table->snap_items[index], 0, sizeof(struct snap)); while(index < table->tbl_count - 1) { struct snap *item = &table->snap_items[index]; item->time = table->snap_items[index + 1].time; item->flags = table->snap_items[index + 1].flags; item->gen = table->snap_items[index + 1].gen; item->index = table->snap_items[index + 1].index; memcpy(&item->name[0], &table->snap_items[index + 1].name[0], SNAP_MAX_NAMELEN); index ++; } table->tbl_count --; disk_snap_table->magic = cpu_to_le32((__u32)DISK_SNAP_TABLE_MAGIC); disk_snap_table->count = cpu_to_le32((__u32)table->tbl_count - 1); disk_snap_table->generation = cpu_to_le32((__u32)table->generation + 1); memset(&disk_snap_table->snap_items[0], 0, SNAP_MAX * sizeof(struct snap_disk)); for (i = 0; i < table->tbl_count - 1; i++) { struct snap_disk *disk_item = &disk_snap_table->snap_items[i]; struct snap *item = &table->snap_items[i+1]; disk_item[i].time = cpu_to_le64((__u64)item->time); disk_item[i].gen = cpu_to_le32((__u32)item->gen); disk_item[i].flags = cpu_to_le32((__u32)item->flags); disk_item[i].index = cpu_to_le32((__u32)item->index); memcpy(&disk_item[i].name , item->name, SNAP_MAX_NAMELEN); } rc = snapops->set_meta_attr(cache->cache_sb, DISK_SNAPTABLE_ATTR, (char*)disk_snap_table, sizeof(struct snap_disk_table)); SNAP_FREE(disk_snap_table, sizeof(struct snap_disk_table)); up(&table->tbl_sema); RETURN(rc); } int snapfs_read_snaptable(struct snap_cache *cache, int tableno) { struct snap_table *table; struct snap_disk_table *disk_snap_table; struct snapshot_operations *snapops; int i, rc; int size = 0; snapops = filter_c2csnapops(cache->cache_filter); if (!snapops || !snapops->get_meta_attr) RETURN(-EINVAL); SNAP_ALLOC(disk_snap_table, sizeof(struct snap_disk_table)); size = sizeof(struct snap_disk_table); table = &snap_tables[tableno]; memset(table, 0, sizeof(struct snap_table)); init_MUTEX(&table->tbl_sema); /*Initialized table */ table->tbl_count = 1; rc = snapops->get_meta_attr(cache->cache_sb, DISK_SNAPTABLE_ATTR, (char*)disk_snap_table, &size); if (rc < 0) { SNAP_FREE(disk_snap_table, sizeof(struct snap_disk_table)); RETURN(rc); } if (le32_to_cpu(disk_snap_table->magic) != DISK_SNAP_TABLE_MAGIC) { CERROR("On disk snaptable is not right \n"); RETURN(rc); } table->generation = le32_to_cpu(disk_snap_table->generation); table->tbl_count += le32_to_cpu(disk_snap_table->count); for ( i = 0; i < disk_snap_table->count; i++) { struct snap *item = &table->snap_items[i + 1]; item->time = le64_to_cpu(disk_snap_table->snap_items[i].time); item->gen = le32_to_cpu(disk_snap_table->snap_items[i].gen); item->flags = le32_to_cpu(disk_snap_table->snap_items[i].flags); item->index = le32_to_cpu(disk_snap_table->snap_items[i].index); memcpy(&item->name[0], &disk_snap_table->snap_items[i].name[0], SNAP_MAX_NAMELEN); } SNAP_FREE(disk_snap_table, sizeof(struct snap_disk_table)); return 0; } static int getdata(struct ioc_data *input, void **karg) { void *tmp = NULL; if (!input->ioc_inlen || !input->ioc_inbuf) return 0; SNAP_ALLOC(tmp, input->ioc_inlen); if (!tmp) RETURN(-ENOMEM); CDEBUG(D_SNAP, "snap_alloc:len %d, add %p\n", input->ioc_inlen, tmp); memset(tmp, 0, input->ioc_inlen); if (copy_from_user(tmp, input->ioc_inbuf, input->ioc_inlen)) { CERROR("get inbuf data error \n"); SNAP_FREE(tmp, input->ioc_inlen); RETURN(-EFAULT); } *karg = tmp; return 0; } static inline void freedata(void *data, struct ioc_data *input) { SNAP_FREE(data, input->ioc_inlen); CDEBUG(D_SNAP, "snap_free:len %d, add %p\n", input->ioc_inlen, data); } static int get_next_inode(struct inode *pri, void *ino) { static ino_t prev_ino = -1 ; ino_t this_ino = pri->i_ino; ino_t find_ino = *(ino_t *)ino; ino_t *found = ino; if( find_ino == 0) { (*found) = this_ino; return -1; } if( find_ino == prev_ino ) { (*found) = this_ino; return -1; } else { prev_ino = this_ino; } return 0; } static int snap_get_next_inode(struct snap_ino_list_data *data, ino_t *found_ino, ino_t *parent_ino) { kdev_t dev = data->dev; ino_t this_ino = data->ino; struct snap_cache *cache; struct inode *inode; struct dentry * dentry; ENTRY; cache = snap_find_cache(dev); if ( !cache ) { EXIT; return -EINVAL; } snap_iterate( cache->cache_sb, &get_next_inode, NULL, &(data->ino), SNAP_ITERATE_COWED_INODE); if( data->ino == this_ino ) { data->ino = 0; } *found_ino = data->ino; if( !(*found_ino) ) return 0; *parent_ino = 0; inode = iget (cache->cache_sb, *found_ino); if (list_empty(&inode->i_dentry)) { CERROR("No dentry for ino %lu, Error(XXX)! \n", inode->i_ino); iput(inode); return 0; } else { dentry = dget(list_entry(inode->i_dentry.next, struct dentry, d_alias)); } if( dentry->d_parent->d_inode) *parent_ino = dentry->d_parent->d_inode->i_ino; else *parent_ino = 0; dput(dentry); iput(inode); return 0; } static int print_inode(struct inode *pri,void *param) { CDEBUG(D_SNAP, "cowed inode list: ino %lu \n", pri->i_ino); return 0; } static int snap_print(struct super_block *sb, void *data) { snap_iterate(sb, &print_inode, NULL, data, SNAP_ITERATE_COWED_INODE); return 0; } static int delete_new_inode(struct inode *pri, void *param) { struct snap_iterdata * data; int index = 1; time_t restore_time = 0xFFFFFFFF; ENTRY; if(!pri) RETURN(0); if(snap_is_redirector(pri)) RETURN(0); data = (struct snap_iterdata*) param; if(data) { index = data->index; restore_time = data->time; } CDEBUG(D_SNAP, "ino %lu, index=%d, time=%lu\n", pri->i_ino, index, restore_time); if( pri->i_mtime > restore_time || pri->i_ctime > restore_time ) { struct list_head *head = &pri->i_dentry, *pos; CDEBUG(D_SNAP, "snap_restore ino %lu is newer, delete \n",pri->i_ino); for( pos = head->next; pos != head; pos = pos->next ){ d_drop( list_entry(pos, struct dentry, d_alias) ); } pri->i_nlink = 0; } RETURN(0); } static int restore_inode(struct inode *pri, void *param) { struct snap_iterdata * data; int tableno = 0; int index = 1; time_t restore_time = 0xFFFFFFFF; struct inode *ind = NULL; int slot; int restore_slot; struct snap_table *table; int restore_index; ENTRY; if(!pri) RETURN(0); data = (struct snap_iterdata*) param; if(data) { index = data->index; tableno = data->tableno; restore_time = data->time; } CDEBUG(D_SNAP, "ino %lu, index=%d, time=%lu, tableno %d\n", pri->i_ino, index, restore_time, tableno); /* XXX: should we have = here? */ if(pri->i_mtime > restore_time || pri->i_ctime > restore_time) { restore_index = index; table = &snap_tables[tableno]; /* first find if there are indirected at the index */ ind = snap_get_indirect(pri, NULL, index); /* if not found, get the FIRST index after this and before NOW*/ /* XXX fix this later, now use tbl_count, not NOW */ if(!ind) { restore_slot = snap_index2slot(table, index); for(slot = restore_slot; slot <= table->tbl_count; slot++) { ind = snap_get_indirect (pri, NULL, table->snap_items[slot].index); if(ind) { restore_index = table->snap_items[slot].index; break; } } } if(ind) { CDEBUG(D_SNAP, "restore ino %lu with index %d\n", pri->i_ino, restore_index); iput(ind); snap_restore_indirect(pri, restore_index); /* XXX */ //delete_inode(pri, param); snap_destroy_indirect(pri, restore_index, NULL); } else { CDEBUG(D_SNAP, "ERROR:restore ino %lu\n", pri->i_ino); } } else { CDEBUG(D_SNAP, "ino %lu is older, don't restore\n", pri->i_ino); } RETURN(0); } //int snap_restore(struct super_block *sb, void *data) static int snap_restore(struct super_block *sb, struct snap_iterdata *data) { CDEBUG(D_SNAP, "dev %d, tableno %d, index %d, time %lu\n", data->dev, data->tableno, data->index, data->time ); snap_iterate(sb, &delete_new_inode, NULL, data, SNAP_ITERATE_ALL_INODE); snap_iterate(sb, &restore_inode, NULL, data, SNAP_ITERATE_COWED_INODE ); return 0; } /* return the index number of a name in a table */ int snap_get_index_from_name(int tableno, char *name) { struct snap_table *table; int slot; if ( tableno < 0 || tableno > SNAP_MAX_TABLES ) { CERROR("invalid table number %d\n", tableno); return -EINVAL; } table = &snap_tables[tableno]; for ( slot = 0 ; slot < table->tbl_count; slot++) { if (!strcmp(&table->snap_items[slot].name[0], name)) { return table->snap_items[slot].index; } } return -EINVAL; } int snap_iterate_func( struct ioc_snap_tbl_data *data, unsigned int cmd) { struct snapshot_operations *snapops; struct snap_iterdata iterate_data; struct super_block *sb; struct snap_cache *cache; struct snap_table *table; int index, tableno, slot, rc; ENTRY; if (!(cache = snap_find_cache((kdev_t)data->dev))) RETURN(-ENODEV); snapops = filter_c2csnapops(cache->cache_filter); if (!snapops || !snapops->set_meta_attr) RETURN(-EINVAL); tableno = data->no; if (tableno < 0 || tableno > SNAP_MAX_TABLES) { CERROR("invalid table number %d\n", tableno); RETURN(-EINVAL); } sb = cache->cache_sb; table = &snap_tables[tableno]; index = snap_get_index_from_name(tableno, data->snaps[0].name); if (index < 0) { CERROR("Could not find %s in snaptable\n", data->snaps[0].name); RETURN(-EINVAL); } iterate_data.dev = (kdev_t)data->dev; iterate_data.index = index; iterate_data.tableno = tableno; slot = snap_index2slot (table, index); if( slot < 0 ) RETURN(-EINVAL); iterate_data.time = table->snap_items[slot].time; CDEBUG(D_SNAP, "dev %d, tableno %d, index %d, time %lu\n", iterate_data.dev, iterate_data.tableno, iterate_data.index, iterate_data.time); switch (cmd) { case IOC_SNAP_DEBUG: rc = snap_print(sb, &iterate_data); break; case IOC_SNAP_DELETE: rc = snaptable_delete_item(sb, &iterate_data); break; case IOC_SNAP_RESTORE: rc = snap_restore(sb, &iterate_data); break; default: CERROR("unrecognized cmd %d \n", cmd); rc = -EINVAL; break; } RETURN(rc); } #define BUF_SIZE 1024 int snap_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { struct ioc_data input; void *karg = NULL; int rc = 0; kdev_t dev; ENTRY; dev = MINOR(inode->i_rdev); if (dev != SNAP_PSDEV_MINOR) RETURN(-ENODEV); if (!inode) { CDEBUG(D_IOCTL, "invalid inode\n"); RETURN(-EINVAL); } if ( _IOC_TYPE(cmd) != IOC_SNAP_TYPE || _IOC_NR(cmd) < IOC_SNAP_MIN_NR || _IOC_NR(cmd) > IOC_SNAP_MAX_NR ) { /*FIXME: Sometimes Gettimeof the day will come here * still do not know the reason*/ CDEBUG(D_IOCTL, "invalid ioctl ( type %d, nr %d, size %d )\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); RETURN(0); } /* get data structures */ rc = copy_from_user(&input, (void *)arg, sizeof(input)); if (rc) RETURN(rc); /* get data from the input data*/ rc = getdata(&input, &karg); if (rc) RETURN(rc); switch (cmd) { case IOC_SNAP_ADD: { rc = snaptable_add_item(karg); break; } case IOC_SNAP_PRINTTABLE: { struct ioc_data *output; char *tmp; SNAP_ALLOC(tmp, BUF_SIZE); output=(struct ioc_data*)tmp; output->ioc_inbuf = output->ioc_bulk; output->ioc_inlen = BUF_SIZE - sizeof(int) - sizeof(unsigned long); snap_print_table(karg, output->ioc_inbuf, &(output->ioc_inlen)); rc = copy_to_user((char *)arg, output, (output->ioc_inlen + sizeof(int) + sizeof(unsigned long))); SNAP_FREE(tmp, BUF_SIZE); break; } case IOC_SNAP_GETINDEXFROMNAME: { int index = 0; char name[SNAP_MAX_NAMELEN]; int tableno = 0; struct snap_cache *cache; kdev_t dev; int name_len; struct get_index_struct { kdev_t dev; char name[SNAP_MAX_NAMELEN]; }; struct get_index_struct *data = karg; name_len = input.ioc_inlen - sizeof(kdev_t); dev = data->dev; memset(name, 0, SNAP_MAX_NAMELEN); if(name_len > SNAP_MAX_NAMELEN) name_len = SNAP_MAX_NAMELEN; if(name_len < 0 ) name_len = 0; /*for(i=0 ; i< name_len; i++) { name[i] = data->name[i]; } */ memcpy(name, data->name, name_len); printk("dev %d , len %d, name_len %d, find name is [%s]\n", dev, input.ioc_inlen, name_len, name); cache = snap_find_cache(dev); if ( !cache ) { EXIT; rc = -EINVAL; break; } tableno = cache->cache_snap_tableno; index = snap_get_index_from_name(tableno, name); rc = copy_to_user((char *)arg, &index, sizeof(index)); break; } case IOC_SNAP_GET_NEXT_INO: { struct get_ino_struct{ ino_t found_ino; ino_t parent_ino; }get_ino; get_ino.found_ino = 0; get_ino.parent_ino = 0; rc = snap_get_next_inode(karg, &get_ino.found_ino, &get_ino.parent_ino); rc = copy_to_user((char *)arg, &get_ino, sizeof(get_ino)); break; } case IOC_SNAP_GET_INO_INFO: { struct ioc_ino_info{ kdev_t dev; ino_t ino; int index; }; struct snap_cache *cache; struct inode *pri; struct inode *ind; struct ioc_ino_info *data = karg; ino_t ind_ino = 0; cache = snap_find_cache(data->dev); if ( !cache ) { EXIT; rc = -EINVAL; break; } printk("get_ino_info, dev %d, ino %lu, index %d\n", data->dev, data->ino, data->index); pri = iget(cache->cache_sb, data->ino); ind = snap_get_indirect(pri, NULL, data->index); if(ind) { ind_ino = ind->i_ino; iput(ind); } iput(pri); printk("get_ino_info, get ind %lu\n", ind_ino); rc = copy_to_user((char *)arg, &ind_ino, sizeof(ino_t)); break; } case IOC_SNAP_DELETE: case IOC_SNAP_RESTORE: case IOC_SNAP_DEBUG: rc = snap_iterate_func(karg, cmd); break; #ifdef SNAP_DEBUG case IOC_SNAP_DEVFAIL: snap_debug_failcode = (unsigned int)arg; break; #endif case IOC_SNAP_SHOW_DOTSNAP: { struct ioc_show_info{ kdev_t dev; int show; }; struct snap_cache *cache; struct ioc_show_info *data = karg; cache = snap_find_cache(data->dev); if( !cache ) { EXIT; rc = -EINVAL; break; } cache->cache_show_dotsnap = (char)data->show; CDEBUG(D_IOCTL, "Set show dotsnap: %s\n", data->show ? "Yes" : "No"); break; } default: rc = -EINVAL; break; } freedata(karg, &input); RETURN(rc); }