Пример #1
0
static void ext2_clear_inode(struct inode *inode)
{
	struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;
	ext2_discard_reservation(inode);
	EXT2_I(inode)->i_block_alloc_info = NULL;
	if (unlikely(rsv))
		kfree(rsv);
}
Пример #2
0
/*
 * Called when filp is released. This happens when all file descriptors
 * for a single struct file are closed. Note that different open() calls
 * for the same file yield different struct file structures.
 */
static int ext2_release_file (struct inode * inode, struct file * filp)
{
	if (filp->f_mode & FMODE_WRITE) {
		mutex_lock(&EXT2_I(inode)->truncate_mutex);
		ext2_discard_reservation(inode);
		mutex_unlock(&EXT2_I(inode)->truncate_mutex);
	}
	return 0;
}
Пример #3
0
/*
 * COMP3301 Addition
 * Write immediate files data to the inode. Covert back to
 * Regular files when the data exceeds the limit.
 */
ssize_t do_immediate_write (struct file* flip, const char __user* buf,
	size_t len, loff_t *ppos, int need_to_encrypt) {

	struct ext2_inode_info *inode_info = EXT2_I(flip->f_dentry->d_inode);
	struct inode *inode = flip->f_dentry->d_inode;
	char *data = (char *)inode_info->i_data;
	char *copy;
	char *ext_inode_data = (char *) (EXT2_I(inode)->i_data);
	int err;
	ssize_t result;


	if (*ppos + len >= IMMEDIATE_FILE_SIZE) {
		// Convert to regular file

       	copy = (char *) kmalloc(sizeof(char) * strlen(ext_inode_data)
       	 	+ 1, GFP_KERNEL);
       	memset(copy, 0, strlen(ext_inode_data) + 1);
       	memcpy(copy, ext_inode_data, strlen(ext_inode_data));
       	copy[strlen(ext_inode_data)] = 0;


       	inode->i_mode &= ~(S_IF_IMMEDIATE & S_IFMT);
       	inode->i_mode |= S_IFREG & S_IFMT;

        inode_info->i_data[0] = ext2_new_block(inode, 0, &err);
        mark_inode_dirty(inode);

       	flip->f_pos = 0;
       	result = write_encrypt(flip, copy, strlen(copy), &flip->f_pos);
       	result = write_encrypt(flip, buf, len, ppos);

       	kfree(copy);
        return result;
	}

    if (need_to_encrypt) {
    	encrypt(buf, len);
    }

	if (copy_from_user(data + *ppos, buf, len)) {
		return -1;
	}

    *ppos += len;
    flip->f_pos = *ppos;
    inode->i_size += len;
    mark_inode_dirty(inode);

	return len;
}
Пример #4
0
/*
 * inode->i_sem: don't care
 */
static struct posix_acl *
ext2_get_acl(struct inode *inode, int type)
{
	struct ext2_inode_info *ei = EXT2_I(inode);
	int name_index;
	char *value = NULL;
	struct posix_acl *acl;
	int retval;

	if (!test_opt(inode->i_sb, POSIX_ACL))
		return 0;

	switch(type) {
		case ACL_TYPE_ACCESS:
			acl = ext2_iget_acl(inode, &ei->i_acl);
			if (acl != EXT2_ACL_NOT_CACHED)
				return acl;
			name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
			break;

		case ACL_TYPE_DEFAULT:
			acl = ext2_iget_acl(inode, &ei->i_default_acl);
			if (acl != EXT2_ACL_NOT_CACHED)
				return acl;
			name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
			break;

		default:
			return ERR_PTR(-EINVAL);
	}
	retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
	if (retval > 0) {
		value = kmalloc(retval, GFP_KERNEL);
		if (!value)
			return ERR_PTR(-ENOMEM);
		retval = ext2_xattr_get(inode, name_index, "", value, retval);
	}
	if (retval > 0)
		acl = ext2_acl_from_disk(value, retval);
	else if (retval == -ENODATA || retval == -ENOSYS)
		acl = NULL;
	else
		acl = ERR_PTR(retval);
	if (value)
		kfree(value);

	if (!IS_ERR(acl)) {
		switch(type) {
			case ACL_TYPE_ACCESS:
				ext2_iset_acl(inode, &ei->i_acl, acl);
				break;

			case ACL_TYPE_DEFAULT:
				ext2_iset_acl(inode, &ei->i_default_acl, acl);
				break;
		}
	}
	return acl;
}
Пример #5
0
static int ext2_symlink (struct inode * dir, struct dentry * dentry,
                         const char * symname)
{
    struct super_block * sb = dir->i_sb;
    int err = -ENAMETOOLONG;
    unsigned l = strlen(symname)+1;
    struct inode * inode;

    if (l > sb->s_blocksize)
        goto out;

    dquot_initialize(dir);

    inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &dentry->d_name);
    err = PTR_ERR(inode);
    if (IS_ERR(inode))
        goto out;

    if (l > sizeof (EXT2_I(inode)->i_data)) {
        /* slow symlink */
        inode->i_op = &ext2_symlink_inode_operations;
        if (test_opt(inode->i_sb, NOBH))
            inode->i_mapping->a_ops = &ext2_nobh_aops;
        else
            inode->i_mapping->a_ops = &ext2_aops;
        err = page_symlink(inode, symname, l);
        if (err)
            goto out_fail;
    } else {
        /* fast symlink */
        inode->i_op = &ext2_fast_symlink_inode_operations;
        memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
        inode->i_size = l-1;
    }
    mark_inode_dirty(inode);

    err = ext2_add_nondir(dentry, inode);
out:
    return err;

out_fail:
    inode_dec_link_count(inode);
    unlock_new_inode(inode);
    iput (inode);
    goto out;
}
Пример #6
0
static void ext2_clear_inode(struct inode *inode)
{
	struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;
#ifdef CONFIG_EXT2_FS_POSIX_ACL
	struct ext2_inode_info *ei = EXT2_I(inode);

	if (ei->i_acl && ei->i_acl != EXT2_ACL_NOT_CACHED) {
		posix_acl_release(ei->i_acl);
		ei->i_acl = EXT2_ACL_NOT_CACHED;
	}
	if (ei->i_default_acl && ei->i_default_acl != EXT2_ACL_NOT_CACHED) {
		posix_acl_release(ei->i_default_acl);
		ei->i_default_acl = EXT2_ACL_NOT_CACHED;
	}
#endif
	ext2_discard_reservation(inode);
	EXT2_I(inode)->i_block_alloc_info = NULL;
	if (unlikely(rsv))
		kfree(rsv);
}
Пример #7
0
/*
 * Called at the last iput() if i_nlink is zero.
 */
void ext2_evict_inode(struct inode * inode)
{
	struct ext2_block_alloc_info *rsv;
	int want_delete = 0;

	if (!inode->i_nlink && !is_bad_inode(inode)) {
		want_delete = 1;
		dquot_initialize(inode);
	} else {
		dquot_drop(inode);
	}

	truncate_inode_pages_final(&inode->i_data);

	if (want_delete) {
		sb_start_intwrite(inode->i_sb);
		/* set dtime */
		EXT2_I(inode)->i_dtime	= get_seconds();
		mark_inode_dirty(inode);
		__ext2_write_inode(inode, inode_needs_sync(inode));
		/* truncate to 0 */
		inode->i_size = 0;
		if (inode->i_blocks)
			ext2_truncate_blocks(inode, 0);
		ext2_xattr_delete_inode(inode);
	}

	invalidate_inode_buffers(inode);
	clear_inode(inode);

	ext2_discard_reservation(inode);
	rsv = EXT2_I(inode)->i_block_alloc_info;
	EXT2_I(inode)->i_block_alloc_info = NULL;
	if (unlikely(rsv))
		kfree(rsv);

	if (want_delete) {
		ext2_free_inode(inode);
		sb_end_intwrite(inode->i_sb);
	}
}
Пример #8
0
// Vijay: Modified lookup for ext2bp. This includes a validation check inside it.
static struct dentry *ext2bp_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
    ext2bp_debug("Inside ext2bp_lookup for dir with inode num: %lu\n", dir->i_ino);
    struct inode * inode;
    ino_t ino;

    if (dentry->d_name.len > EXT2_NAME_LEN)
        return ERR_PTR(-ENAMETOOLONG);

    ino = ext2_inode_by_name(dir, dentry);

    inode = NULL;
    if (ino) {
        inode = ext2_iget(dir->i_sb, ino);
        if (IS_ERR(inode))
            return ERR_CAST(inode);

        // Check the inode state
        if (inode->i_state & I_DIRTY)
            ext2bp_debug("Inode's state: dirty\n");
        if (inode->i_state & I_NEW)
            ext2bp_debug("Inode's state: new\n");
        if (inode->i_state & I_LOCK)
            ext2bp_debug("Inode's state: locked\n");
        if (inode->i_state & I_CLEAR)
            ext2bp_debug("Inode's state: clear\n");
        if (inode->i_state & I_SYNC)
            ext2bp_debug("Inode's state: sync\n");

        ext2bp_debug("Inode's state: something I didnt check for\n");

        // Now that we have the child inode, we can
        // carry out the check.
        struct ext2_inode_info *ei = EXT2_I(inode);
        int n;
        int backlink_present = 0;
        for (n=0; n < EXT2_N_LINKS; n++)
            if (ei->i_backlinks[n] == dir->i_ino) {
                backlink_present = 1;
                ext2bp_debug("Found backlink from %lu to %lu\n", inode->i_ino, dir->i_ino);
                break;
            }
        if (!backlink_present) {
            printk("Vijay:Error:Did not find backlink from %lu to %lu\n", inode->i_ino, dir->i_ino);
            iput(inode);
            return ERR_PTR(-EIO);
        }
    }
    return d_splice_alias(inode, dentry);
}
Пример #9
0
static void ext2_clear_inode(struct inode *inode)
{
#ifdef CONFIG_EXT2_FS_POSIX_ACL
	struct ext2_inode_info *ei = EXT2_I(inode);

	if (ei->i_acl && ei->i_acl != EXT2_ACL_NOT_CACHED) {
		posix_acl_release(ei->i_acl);
		ei->i_acl = EXT2_ACL_NOT_CACHED;
	}
	if (ei->i_default_acl && ei->i_default_acl != EXT2_ACL_NOT_CACHED) {
		posix_acl_release(ei->i_default_acl);
		ei->i_default_acl = EXT2_ACL_NOT_CACHED;
	}
#endif
}
Пример #10
0
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
    struct ext2_inode_info *ei = EXT2_I(inode);
    write_lock(&ei->i_meta_lock);
    if (ei->i_prealloc_count) {
        unsigned short total = ei->i_prealloc_count;
        unsigned long block = ei->i_prealloc_block;
        ei->i_prealloc_count = 0;
        ei->i_prealloc_block = 0;
        write_unlock(&ei->i_meta_lock);
        ext2_free_blocks (inode, block, total);
        return;
    } else
        write_unlock(&ei->i_meta_lock);
#endif
}
Пример #11
0
/*
 * Called at the last iput() if i_nlink is zero.
 */
void ext2_delete_inode (struct inode * inode)
{
	if (is_bad_inode(inode))
		goto no_delete;
	EXT2_I(inode)->i_dtime	= get_seconds();
	mark_inode_dirty(inode);
	ext2_update_inode(inode, inode_needs_sync(inode));

	inode->i_size = 0;
	if (inode->i_blocks)
		ext2_truncate (inode);
	ext2_free_inode (inode);

	return;
no_delete:
	clear_inode(inode);	/* We must guarantee clearing of inode... */
}
Пример #12
0
static int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
    struct inode * inode = dentry->d_inode;
    struct ext2_dir_entry_2 * de;
    struct page * page;
    int err = -ENOENT;

    de = ext2_find_entry (dir, dentry, &page);
    if (!de)
        goto out;

    err = ext2_delete_entry (de, page);
    if (err)
        goto out;

    inode->i_ctime = dir->i_ctime;

    // Removing the backlink from the inode
    ext2bp_debug("Removing backlink from the inode\n");
    int i, link_pos = -1;
    struct ext2_inode_info *ei = EXT2_I(inode);
    for(i=0; i < inode->i_nlink; i++) {
        if (ei->i_backlinks[i] == dir->i_ino) {
            link_pos = i;
            ext2bp_debug("Found backlink to inode %d at position %d\n", dir->i_ino, i);
            break;
        }
    }

    // If we find the backlink, remove it and move the other
    // backlinks up a bit.
    if (link_pos != -1) {
        for(i=link_pos; i < (EXT2_N_LINKS - 1); i++) {
            ei->i_backlinks[i] = ei->i_backlinks[i+1];
        }
    }
    mark_inode_dirty(inode);

    inode_dec_link_count(inode);
    err = 0;
out:
    return err;
}
Пример #13
0
/*
 * The lock ordering for ext2 DAX fault paths is:
 *
 * mmap_sem (MM)
 *   sb_start_pagefault (vfs, freeze)
 *     ext2_inode_info->dax_sem
 *       address_space->i_mmap_rwsem or page_lock (mutually exclusive in DAX)
 *         ext2_inode_info->truncate_mutex
 *
 * The default page_lock and i_size verification done by non-DAX fault paths
 * is sufficient because ext2 doesn't support hole punching.
 */
static vm_fault_t ext2_dax_fault(struct vm_fault *vmf)
{
	struct inode *inode = file_inode(vmf->vma->vm_file);
	struct ext2_inode_info *ei = EXT2_I(inode);
	vm_fault_t ret;

	if (vmf->flags & FAULT_FLAG_WRITE) {
		sb_start_pagefault(inode->i_sb);
		file_update_time(vmf->vma->vm_file);
	}
	down_read(&ei->dax_sem);

	ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, NULL, &ext2_iomap_ops);

	up_read(&ei->dax_sem);
	if (vmf->flags & FAULT_FLAG_WRITE)
		sb_end_pagefault(inode->i_sb);
	return ret;
}
Пример #14
0
/*
 * The lock ordering for ext2 DAX fault paths is:
 *
 * mmap_sem (MM)
 *   sb_start_pagefault (vfs, freeze)
 *     ext2_inode_info->dax_sem
 *       address_space->i_mmap_rwsem or page_lock (mutually exclusive in DAX)
 *         ext2_inode_info->truncate_mutex
 *
 * The default page_lock and i_size verification done by non-DAX fault paths
 * is sufficient because ext2 doesn't support hole punching.
 */
static int ext2_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
	struct inode *inode = file_inode(vma->vm_file);
	struct ext2_inode_info *ei = EXT2_I(inode);
	int ret;

	if (vmf->flags & FAULT_FLAG_WRITE) {
		sb_start_pagefault(inode->i_sb);
		file_update_time(vma->vm_file);
	}
	down_read(&ei->dax_sem);

	ret = __dax_fault(vma, vmf, ext2_get_block, NULL);

	up_read(&ei->dax_sem);
	if (vmf->flags & FAULT_FLAG_WRITE)
		sb_end_pagefault(inode->i_sb);
	return ret;
}
Пример #15
0
static int ext2_dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
						pmd_t *pmd, unsigned int flags)
{
	struct inode *inode = file_inode(vma->vm_file);
	struct ext2_inode_info *ei = EXT2_I(inode);
	int ret;

	if (flags & FAULT_FLAG_WRITE) {
		sb_start_pagefault(inode->i_sb);
		file_update_time(vma->vm_file);
	}
	down_read(&ei->dax_sem);

	ret = __dax_pmd_fault(vma, addr, pmd, flags, ext2_get_block, NULL);

	up_read(&ei->dax_sem);
	if (flags & FAULT_FLAG_WRITE)
		sb_end_pagefault(inode->i_sb);
	return ret;
}
Пример #16
0
static int ext2_quota_on(struct super_block *sb, int type, int format_id,
			 const struct path *path)
{
	int err;
	struct inode *inode;

	err = dquot_quota_on(sb, type, format_id, path);
	if (err)
		return err;

	inode = d_inode(path->dentry);
	inode_lock(inode);
	EXT2_I(inode)->i_flags |= EXT2_NOATIME_FL | EXT2_IMMUTABLE_FL;
	inode_set_flags(inode, S_NOATIME | S_IMMUTABLE,
			S_NOATIME | S_IMMUTABLE);
	inode_unlock(inode);
	mark_inode_dirty(inode);

	return 0;
}
Пример #17
0
static int ext2_quota_off(struct super_block *sb, int type)
{
	struct inode *inode = sb_dqopt(sb)->files[type];
	int err;

	if (!inode || !igrab(inode))
		goto out;

	err = dquot_quota_off(sb, type);
	if (err)
		goto out_put;

	inode_lock(inode);
	EXT2_I(inode)->i_flags &= ~(EXT2_NOATIME_FL | EXT2_IMMUTABLE_FL);
	inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE);
	inode_unlock(inode);
	mark_inode_dirty(inode);
out_put:
	iput(inode);
	return err;
out:
	return dquot_quota_off(sb, type);
}
Пример #18
0
static int ext2_dax_pfn_mkwrite(struct vm_area_struct *vma,
		struct vm_fault *vmf)
{
	struct inode *inode = file_inode(vma->vm_file);
	struct ext2_inode_info *ei = EXT2_I(inode);
	loff_t size;
	int ret;

	sb_start_pagefault(inode->i_sb);
	file_update_time(vma->vm_file);
	down_read(&ei->dax_sem);

	/* check that the faulting page hasn't raced with truncate */
	size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT;
	if (vmf->pgoff >= size)
		ret = VM_FAULT_SIGBUS;
	else
		ret = dax_pfn_mkwrite(vma, vmf);

	up_read(&ei->dax_sem);
	sb_end_pagefault(inode->i_sb);
	return ret;
}
Пример #19
0
/*
 * COMP3301 Addition
 * Read from immediate files with data stored directly in the inode.
 */
ssize_t do_immediate_read (struct file* flip, const char __user* buf,
	size_t len, loff_t *ppos) {

    struct inode *inode = flip->f_dentry->d_inode;
    struct ext2_inode_info *inode_info = EXT2_I(inode);
    char* new_buffer = (char *) kmalloc(sizeof(char) * len, GFP_KERNEL);
    char *data = (char *)inode_info->i_data;
    copy_buffer(new_buffer, data, len);

    if (*ppos + len > inode->i_size) {
    	len -= ((*ppos + len) - inode->i_size);
    }

	if (copy_to_user(buf, new_buffer + *ppos, len)) {
		kfree(new_buffer);
		return -1;
	}

	kfree(new_buffer);
	*ppos += len;

    return len;
}
Пример #20
0
static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
{
#ifdef EXT2FS_DEBUG
    static unsigned long alloc_hits, alloc_attempts;
#endif
    unsigned long result;


#ifdef EXT2_PREALLOCATE
    struct ext2_inode_info *ei = EXT2_I(inode);
    write_lock(&ei->i_meta_lock);
    if (ei->i_prealloc_count &&
            (goal == ei->i_prealloc_block || goal + 1 == ei->i_prealloc_block))
    {
        result = ei->i_prealloc_block++;
        ei->i_prealloc_count--;
        write_unlock(&ei->i_meta_lock);
        ext2_debug ("preallocation hit (%lu/%lu).\n",
                    ++alloc_hits, ++alloc_attempts);
    } else {
        write_unlock(&ei->i_meta_lock);
        ext2_discard_prealloc (inode);
        ext2_debug ("preallocation miss (%lu/%lu).\n",
                    alloc_hits, ++alloc_attempts);
        if (S_ISREG(inode->i_mode))
            result = ext2_new_block (inode, goal,
                                     &ei->i_prealloc_count,
                                     &ei->i_prealloc_block, err);
        else
            result = ext2_new_block(inode, goal, NULL, NULL, err);
    }
#else
    result = ext2_new_block (inode, goal, 0, 0, err);
#endif
    return result;
}
Пример #21
0
static void ext2_free_in_core_inode(struct inode *inode)
{
	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
}
Пример #22
0
static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
                        struct inode * new_dir,	struct dentry * new_dentry )
{
    struct inode * old_inode = old_dentry->d_inode;
    struct inode * new_inode = new_dentry->d_inode;

    // Removing the old dir from the backlinks of the inode
    int i, link_pos = -1;
    struct ext2_inode_info *ei;
    // Use the new one if it is there
    if(new_inode) ei = EXT2_I(new_inode);
    else ei = EXT2_I(old_inode);

    for(i=0; i < EXT2_N_LINKS; i++) {
        if (ei->i_backlinks[i] == old_dir->i_ino) {
            link_pos = i;
            break;
        }
    }

    // If we find the backlink, remove it and move the other
    // backlinks up a bit.
    if (link_pos != -1) {
        for(i=link_pos; i < (EXT2_N_LINKS - 1); i++) {
            ei->i_backlinks[i] = ei->i_backlinks[i+1];
        }
    }

    struct page * dir_page = NULL;
    struct ext2_dir_entry_2 * dir_de = NULL;
    struct page * old_page;
    struct ext2_dir_entry_2 * old_de;
    int err = -ENOENT;

    old_de = ext2_find_entry (old_dir, old_dentry, &old_page);
    if (!old_de)
        goto out;

    if (S_ISDIR(old_inode->i_mode)) {
        err = -EIO;
        dir_de = ext2_dotdot(old_inode, &dir_page);
        if (!dir_de)
            goto out_old;
    }

    if (new_inode) {
        struct page *new_page;
        struct ext2_dir_entry_2 *new_de;

        err = -ENOTEMPTY;
        if (dir_de && !ext2_empty_dir (new_inode))
            goto out_dir;

        err = -ENOENT;
        new_de = ext2_find_entry (new_dir, new_dentry, &new_page);
        if (!new_de)
            goto out_dir;
        inode_inc_link_count(old_inode);
        ext2_set_link(new_dir, new_de, new_page, old_inode);
        new_inode->i_ctime = CURRENT_TIME_SEC;
        if (dir_de)
            drop_nlink(new_inode);
        inode_dec_link_count(new_inode);
    } else {
        if (dir_de) {
            err = -EMLINK;
            if (new_dir->i_nlink >= EXT2_LINK_MAX)
                goto out_dir;
        }
        inode_inc_link_count(old_inode);
        err = ext2_add_link(new_dentry, old_inode);
        if (err) {
            inode_dec_link_count(old_inode);
            goto out_dir;
        }
        if (dir_de)
            inode_inc_link_count(new_dir);
    }

    /*
     * Like most other Unix systems, set the ctime for inodes on a
     * rename.
     * inode_dec_link_count() will mark the inode dirty.
     */
    old_inode->i_ctime = CURRENT_TIME_SEC;

    ext2_delete_entry (old_de, old_page);
    inode_dec_link_count(old_inode);

    if (dir_de) {
        ext2_set_link(old_inode, dir_de, dir_page, new_dir);
        inode_dec_link_count(old_dir);
    }
    return 0;


out_dir:
    if (dir_de) {
        kunmap(dir_page);
        page_cache_release(dir_page);
    }
out_old:
    kunmap(old_page);
    page_cache_release(old_page);
out:
    return err;
}
Пример #23
0
static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
	struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
	nd_set_link(nd, (char *)ei->i_data);
	return NULL;
}
Пример #24
0
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct inode *inode = file_inode(filp);
	struct ext2_inode_info *ei = EXT2_I(inode);
	unsigned int flags;
	unsigned short rsv_window_size;
	int ret;

	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);

	switch (cmd) {
	case EXT2_IOC_GETFLAGS:
		ext2_get_inode_flags(ei);
		flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
		return put_user(flags, (int __user *) arg);
	case EXT2_IOC_SETFLAGS: {
		unsigned int oldflags;

		ret = mnt_want_write_file(filp);
		if (ret)
			return ret;

		if (!inode_owner_or_capable(inode)) {
			ret = -EACCES;
			goto setflags_out;
		}

		if (get_user(flags, (int __user *) arg)) {
			ret = -EFAULT;
			goto setflags_out;
		}

		flags = ext2_mask_flags(inode->i_mode, flags);

		inode_lock(inode);
		/* Is it quota file? Do not allow user to mess with it */
		if (IS_NOQUOTA(inode)) {
			inode_unlock(inode);
			ret = -EPERM;
			goto setflags_out;
		}
		oldflags = ei->i_flags;

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 *
		 * This test looks nicer. Thanks to Pauline Middelink
		 */
		if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
			if (!capable(CAP_LINUX_IMMUTABLE)) {
				inode_unlock(inode);
				ret = -EPERM;
				goto setflags_out;
			}
		}

		flags = flags & EXT2_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
		ei->i_flags = flags;

		ext2_set_inode_flags(inode);
		inode->i_ctime = current_time(inode);
		inode_unlock(inode);

		mark_inode_dirty(inode);
setflags_out:
		mnt_drop_write_file(filp);
		return ret;
	}
	case EXT2_IOC_GETVERSION:
		return put_user(inode->i_generation, (int __user *) arg);
	case EXT2_IOC_SETVERSION: {
		__u32 generation;

		if (!inode_owner_or_capable(inode))
			return -EPERM;
		ret = mnt_want_write_file(filp);
		if (ret)
			return ret;
		if (get_user(generation, (int __user *) arg)) {
			ret = -EFAULT;
			goto setversion_out;
		}

		inode_lock(inode);
		inode->i_ctime = current_time(inode);
		inode->i_generation = generation;
		inode_unlock(inode);

		mark_inode_dirty(inode);
setversion_out:
		mnt_drop_write_file(filp);
		return ret;
	}
	case EXT2_IOC_GETRSVSZ:
		if (test_opt(inode->i_sb, RESERVATION)
			&& S_ISREG(inode->i_mode)
			&& ei->i_block_alloc_info) {
			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
			return put_user(rsv_window_size, (int __user *)arg);
		}
		return -ENOTTY;
	case EXT2_IOC_SETRSVSZ: {

		if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
			return -ENOTTY;

		if (!inode_owner_or_capable(inode))
			return -EACCES;

		if (get_user(rsv_window_size, (int __user *)arg))
			return -EFAULT;

		ret = mnt_want_write_file(filp);
		if (ret)
			return ret;

		if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS)
			rsv_window_size = EXT2_MAX_RESERVE_BLOCKS;

		/*
		 * need to allocate reservation structure for this inode
		 * before set the window size
		 */
		/*
		 * XXX What lock should protect the rsv_goal_size?
		 * Accessed in ext2_get_block only.  ext3 uses i_truncate.
		 */
		mutex_lock(&ei->truncate_mutex);
		if (!ei->i_block_alloc_info)
			ext2_init_block_alloc_info(inode);

		if (ei->i_block_alloc_info){
			struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
			rsv->rsv_goal_size = rsv_window_size;
		}
		mutex_unlock(&ei->truncate_mutex);
		mnt_drop_write_file(filp);
		return 0;
	}
	default:
		return -ENOTTY;
	}
}
Пример #25
0
static void ext2_destroy_inode(struct inode *inode)
{
	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
}
Пример #26
0
struct dentry *ext2_get_parent(struct dentry *child)
{
	struct qstr dotdot = {.name = "..", .len = 2};
	unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot);
	if (!ino)
		return ERR_PTR(-ENOENT);
	return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino));
} 

/*
 * By the time this is called, we already have created
 * the directory cache entry for the new file, but it
 * is so far negative - it has no inode.
 *
 * If the create succeeds, we fill in the inode information
 * with d_instantiate(). 
 */
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
{
	struct inode *inode;

	dquot_initialize(dir);

	inode = ext2_new_inode(dir, mode);
	if (IS_ERR(inode))
		return PTR_ERR(inode);

	inode->i_op = &ext2_file_inode_operations;
	if (ext2_use_xip(inode->i_sb)) {
		inode->i_mapping->a_ops = &ext2_aops_xip;
		inode->i_fop = &ext2_xip_file_operations;
	} else if (test_opt(inode->i_sb, NOBH)) {
		inode->i_mapping->a_ops = &ext2_nobh_aops;
		inode->i_fop = &ext2_file_operations;
	} else {
		inode->i_mapping->a_ops = &ext2_aops;
		inode->i_fop = &ext2_file_operations;
	}
	mark_inode_dirty(inode);
	return ext2_add_nondir(dentry, inode);
}

static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
{
	struct inode * inode;
	int err;

	if (!new_valid_dev(rdev))
		return -EINVAL;

	dquot_initialize(dir);

	inode = ext2_new_inode (dir, mode);
	err = PTR_ERR(inode);
	if (!IS_ERR(inode)) {
		init_special_inode(inode, inode->i_mode, rdev);
#ifdef CONFIG_EXT2_FS_XATTR
		inode->i_op = &ext2_special_inode_operations;
#endif
		mark_inode_dirty(inode);
		err = ext2_add_nondir(dentry, inode);
	}
	return err;
}

static int ext2_symlink (struct inode * dir, struct dentry * dentry,
	const char * symname)
{
	struct super_block * sb = dir->i_sb;
	int err = -ENAMETOOLONG;
	unsigned l = strlen(symname)+1;
	struct inode * inode;

	if (l > sb->s_blocksize)
		goto out;

	dquot_initialize(dir);

	inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO);
	err = PTR_ERR(inode);
	if (IS_ERR(inode))
		goto out;

	if (l > sizeof (EXT2_I(inode)->i_data)) {
		/* slow symlink */
		inode->i_op = &ext2_symlink_inode_operations;
		if (test_opt(inode->i_sb, NOBH))
			inode->i_mapping->a_ops = &ext2_nobh_aops;
		else
			inode->i_mapping->a_ops = &ext2_aops;
		err = page_symlink(inode, symname, l);
		if (err)
			goto out_fail;
	} else {
		/* fast symlink */
		inode->i_op = &ext2_fast_symlink_inode_operations;
		memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
		inode->i_size = l-1;
	}
	mark_inode_dirty(inode);

	err = ext2_add_nondir(dentry, inode);
out:
	return err;

out_fail:
	inode_dec_link_count(inode);
	unlock_new_inode(inode);
	iput (inode);
	goto out;
}

static int ext2_link (struct dentry * old_dentry, struct inode * dir,
	struct dentry *dentry)
{
	struct inode *inode = old_dentry->d_inode;
	int err;

	if (inode->i_nlink >= EXT2_LINK_MAX)
		return -EMLINK;

	dquot_initialize(dir);

	inode->i_ctime = CURRENT_TIME_SEC;
	inode_inc_link_count(inode);
	ihold(inode);

	err = ext2_add_link(dentry, inode);
	if (!err) {
		d_instantiate(dentry, inode);
		return 0;
	}
	inode_dec_link_count(inode);
	iput(inode);
	return err;
}

static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
	struct inode * inode;
	int err = -EMLINK;

	if (dir->i_nlink >= EXT2_LINK_MAX)
		goto out;

	dquot_initialize(dir);

	inode_inc_link_count(dir);

	inode = ext2_new_inode (dir, S_IFDIR | mode);
	err = PTR_ERR(inode);
	if (IS_ERR(inode))
		goto out_dir;

	inode->i_op = &ext2_dir_inode_operations;
	inode->i_fop = &ext2_dir_operations;
	if (test_opt(inode->i_sb, NOBH))
		inode->i_mapping->a_ops = &ext2_nobh_aops;
	else
		inode->i_mapping->a_ops = &ext2_aops;

	inode_inc_link_count(inode);

	err = ext2_make_empty(inode, dir);
	if (err)
		goto out_fail;

	err = ext2_add_link(dentry, inode);
	if (err)
		goto out_fail;

	d_instantiate(dentry, inode);
	unlock_new_inode(inode);
out:
	return err;

out_fail:
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	unlock_new_inode(inode);
	iput(inode);
out_dir:
	inode_dec_link_count(dir);
	goto out;
}

static int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
	struct inode * inode = dentry->d_inode;
	struct ext2_dir_entry_2 * de;
	struct page * page;
	int err = -ENOENT;

	dquot_initialize(dir);

	de = ext2_find_entry (dir, &dentry->d_name, &page);
	if (!de)
		goto out;

	err = ext2_delete_entry (de, page);
	if (err)
		goto out;

	inode->i_ctime = dir->i_ctime;
	inode_dec_link_count(inode);
	err = 0;
out:
	return err;
}

static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
	struct inode * inode = dentry->d_inode;
	int err = -ENOTEMPTY;

	if (ext2_empty_dir(inode)) {
		err = ext2_unlink(dir, dentry);
		if (!err) {
			inode->i_size = 0;
			inode_dec_link_count(inode);
			inode_dec_link_count(dir);
		}
	}
	return err;
}

static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
	struct inode * new_dir,	struct dentry * new_dentry )
{
	struct inode * old_inode = old_dentry->d_inode;
	struct inode * new_inode = new_dentry->d_inode;
	struct page * dir_page = NULL;
	struct ext2_dir_entry_2 * dir_de = NULL;
	struct page * old_page;
	struct ext2_dir_entry_2 * old_de;
	int err = -ENOENT;

	dquot_initialize(old_dir);
	dquot_initialize(new_dir);

	old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page);
	if (!old_de)
		goto out;

	if (S_ISDIR(old_inode->i_mode)) {
		err = -EIO;
		dir_de = ext2_dotdot(old_inode, &dir_page);
		if (!dir_de)
			goto out_old;
	}

	if (new_inode) {
		struct page *new_page;
		struct ext2_dir_entry_2 *new_de;

		err = -ENOTEMPTY;
		if (dir_de && !ext2_empty_dir (new_inode))
			goto out_dir;

		err = -ENOENT;
		new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page);
		if (!new_de)
			goto out_dir;
		ext2_set_link(new_dir, new_de, new_page, old_inode, 1);
		new_inode->i_ctime = CURRENT_TIME_SEC;
		if (dir_de)
			drop_nlink(new_inode);
		inode_dec_link_count(new_inode);
	} else {
		if (dir_de) {
			err = -EMLINK;
			if (new_dir->i_nlink >= EXT2_LINK_MAX)
				goto out_dir;
		}
		err = ext2_add_link(new_dentry, old_inode);
		if (err)
			goto out_dir;
		if (dir_de)
			inode_inc_link_count(new_dir);
	}

	/*
	 * Like most other Unix systems, set the ctime for inodes on a
 	 * rename.
	 */
	old_inode->i_ctime = CURRENT_TIME_SEC;
	mark_inode_dirty(old_inode);

	ext2_delete_entry (old_de, old_page);

	if (dir_de) {
		if (old_dir != new_dir)
			ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0);
		else {
			kunmap(dir_page);
			page_cache_release(dir_page);
		}
		inode_dec_link_count(old_dir);
	}
	return 0;


out_dir:
	if (dir_de) {
		kunmap(dir_page);
		page_cache_release(dir_page);
	}
out_old:
	kunmap(old_page);
	page_cache_release(old_page);
out:
	return err;
}

const struct inode_operations ext2_dir_inode_operations = {
	.create		= ext2_create,
	.lookup		= ext2_lookup,
	.link		= ext2_link,
	.unlink		= ext2_unlink,
	.symlink	= ext2_symlink,
	.mkdir		= ext2_mkdir,
	.rmdir		= ext2_rmdir,
	.mknod		= ext2_mknod,
	.rename		= ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
	.setxattr	= generic_setxattr,
	.getxattr	= generic_getxattr,
	.listxattr	= ext2_listxattr,
	.removexattr	= generic_removexattr,
#endif
	.setattr	= ext2_setattr,
	.check_acl	= ext2_check_acl,
};

const struct inode_operations ext2_special_inode_operations = {
#ifdef CONFIG_EXT2_FS_XATTR
	.setxattr	= generic_setxattr,
	.getxattr	= generic_getxattr,
	.listxattr	= ext2_listxattr,
	.removexattr	= generic_removexattr,
#endif
	.setattr	= ext2_setattr,
	.check_acl	= ext2_check_acl,
};
Пример #27
0
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct inode *inode = filp->f_dentry->d_inode;
	struct ext2_inode_info *ei = EXT2_I(inode);
	unsigned int flags;
	unsigned short rsv_window_size;
	int ret;

	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);

	switch (cmd) {
  case EXT2_FAKE_B_ALLOC:
    /* Fake allocation for ext2 filesystem.
     * */
    {
      struct ext2_fake_b_alloc_arg config;
      struct buffer_head bh_result;
      sector_t iblock, off;
      int ret = 0;

      ret = copy_from_user(&config, (struct ext2_fake_b_alloc_arg __user *)arg,
                           sizeof(struct ext2_fake_b_alloc_arg));
      if (ret != 0) {
        printk (KERN_DEBUG "can't copy from user");
        return -EIO;
      } else ret = 0;

      /* Allocate blocks. */
      off = config.efba_off;
      iblock = config.efba_off >> inode->i_blkbits;
      while ((iblock << inode->i_blkbits) <
                  (config.efba_off + config.efba_size)) {
        memset(&bh_result, 0, sizeof(struct ext2_fake_b_alloc_arg));
        ret = ext2_get_block(inode, iblock, &bh_result, 1);
        if (ret < 0) {
          printk (KERN_DEBUG "get_block_error %d, escaping", ret);
          break;
        }
        iblock++;
      }

      /* Set metadata */
      write_lock(&EXT2_I(inode)->i_meta_lock);
      if (ret == 0) {
        printk (KERN_DEBUG "ok, set size");
        inode->i_size = max_t(loff_t, inode->i_size,
                            config.efba_off + config.efba_size);
      } else if(iblock != config.efba_off >> inode->i_blkbits) {
        /* Partially allocated, size must be fixed.           *
         * But `i_blocks` should containt actual information. */
        inode->i_size = inode->i_blocks << inode->i_blkbits;
      }
      inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
      inode->i_version++;
      write_unlock(&EXT2_I(inode)->i_meta_lock);

      printk(KERN_DEBUG, "returning %d", ret);
      return ret;
    }

	case EXT2_IOC_GETFLAGS:
		ext2_get_inode_flags(ei);
		flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
		return put_user(flags, (int __user *) arg);
	case EXT2_IOC_SETFLAGS: {
		unsigned int oldflags;

		ret = mnt_want_write(filp->f_path.mnt);
		if (ret)
			return ret;

		if (!is_owner_or_cap(inode)) {
			ret = -EACCES;
			goto setflags_out;
		}

		if (get_user(flags, (int __user *) arg)) {
			ret = -EFAULT;
			goto setflags_out;
		}

		flags = ext2_mask_flags(inode->i_mode, flags);

		mutex_lock(&inode->i_mutex);
		/* Is it quota file? Do not allow user to mess with it */
		if (IS_NOQUOTA(inode)) {
			mutex_unlock(&inode->i_mutex);
			ret = -EPERM;
			goto setflags_out;
		}
		oldflags = ei->i_flags;

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 *
		 * This test looks nicer. Thanks to Pauline Middelink
		 */
		if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
			if (!capable(CAP_LINUX_IMMUTABLE)) {
				mutex_unlock(&inode->i_mutex);
				ret = -EPERM;
				goto setflags_out;
			}
		}

		flags = flags & EXT2_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
		ei->i_flags = flags;
		mutex_unlock(&inode->i_mutex);

		ext2_set_inode_flags(inode);
		inode->i_ctime = CURRENT_TIME_SEC;
		mark_inode_dirty(inode);
setflags_out:
		mnt_drop_write(filp->f_path.mnt);
		return ret;
	}
	case EXT2_IOC_GETVERSION:
		return put_user(inode->i_generation, (int __user *) arg);
	case EXT2_IOC_SETVERSION:
		if (!is_owner_or_cap(inode))
			return -EPERM;
		ret = mnt_want_write(filp->f_path.mnt);
		if (ret)
			return ret;
		if (get_user(inode->i_generation, (int __user *) arg)) {
			ret = -EFAULT;
		} else {
			inode->i_ctime = CURRENT_TIME_SEC;
			mark_inode_dirty(inode);
		}
		mnt_drop_write(filp->f_path.mnt);
		return ret;
	case EXT2_IOC_GETRSVSZ:
		if (test_opt(inode->i_sb, RESERVATION)
			&& S_ISREG(inode->i_mode)
			&& ei->i_block_alloc_info) {
			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
			return put_user(rsv_window_size, (int __user *)arg);
		}
		return -ENOTTY;
	case EXT2_IOC_SETRSVSZ: {

		if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
			return -ENOTTY;

		if (!is_owner_or_cap(inode))
			return -EACCES;

		if (get_user(rsv_window_size, (int __user *)arg))
			return -EFAULT;

		ret = mnt_want_write(filp->f_path.mnt);
		if (ret)
			return ret;

		if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS)
			rsv_window_size = EXT2_MAX_RESERVE_BLOCKS;

		/*
		 * need to allocate reservation structure for this inode
		 * before set the window size
		 */
		/*
		 * XXX What lock should protect the rsv_goal_size?
		 * Accessed in ext2_get_block only.  ext3 uses i_truncate.
		 */
		mutex_lock(&ei->truncate_mutex);
		if (!ei->i_block_alloc_info)
			ext2_init_block_alloc_info(inode);

		if (ei->i_block_alloc_info){
			struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
			rsv->rsv_goal_size = rsv_window_size;
		}
		mutex_unlock(&ei->truncate_mutex);
		mnt_drop_write(filp->f_path.mnt);
		return 0;
	}
	default:
		return -ENOTTY;
	}
}
Пример #28
0
int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
		unsigned long arg)
{
	struct ext2_inode_info *ei = EXT2_I(inode);
	unsigned int flags;

	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);

	switch (cmd) {
	case EXT2_IOC_GETFLAGS:
		flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
		return put_user(flags, (int __user *) arg);
	case EXT2_IOC_SETFLAGS: {
		unsigned int oldflags;

		if (IS_RDONLY(inode))
			return -EROFS;

		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
			return -EACCES;

		if (get_user(flags, (int __user *) arg))
			return -EFAULT;

		if (!S_ISDIR(inode->i_mode))
			flags &= ~EXT2_DIRSYNC_FL;

		oldflags = ei->i_flags;

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 *
		 * This test looks nicer. Thanks to Pauline Middelink
		 */
		if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
			if (!capable(CAP_LINUX_IMMUTABLE))
				return -EPERM;
		}

		flags = flags & EXT2_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
		ei->i_flags = flags;

		ext2_set_inode_flags(inode);
		inode->i_ctime = CURRENT_TIME_SEC;
		mark_inode_dirty(inode);
		return 0;
	}
	case EXT2_IOC_GETVERSION:
		return put_user(inode->i_generation, (int __user *) arg);
	case EXT2_IOC_SETVERSION:
		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
			return -EPERM;
		if (IS_RDONLY(inode))
			return -EROFS;
		if (get_user(inode->i_generation, (int __user *) arg))
			return -EFAULT;	
		inode->i_ctime = CURRENT_TIME_SEC;
		mark_inode_dirty(inode);
		return 0;
	default:
		return -ENOTTY;
	}
}
Пример #29
0
/*
 * inode->i_mutex: down
 */
static int
ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
{
	struct ext2_inode_info *ei = EXT2_I(inode);
	int name_index;
	void *value = NULL;
	size_t size = 0;
	int error;

	if (S_ISLNK(inode->i_mode))
		return -EOPNOTSUPP;
	if (!test_opt(inode->i_sb, POSIX_ACL))
		return 0;

	switch(type) {
		case ACL_TYPE_ACCESS:
			name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
			if (acl) {
				mode_t mode = inode->i_mode;
				error = posix_acl_equiv_mode(acl, &mode);
				if (error < 0)
					return error;
				else {
					inode->i_mode = mode;
					mark_inode_dirty(inode);
					if (error == 0)
						acl = NULL;
				}
			}
			break;

		case ACL_TYPE_DEFAULT:
			name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
			if (!S_ISDIR(inode->i_mode))
				return acl ? -EACCES : 0;
			break;

		default:
			return -EINVAL;
	}
 	if (acl) {
		value = ext2_acl_to_disk(acl, &size);
		if (IS_ERR(value))
			return (int)PTR_ERR(value);
	}

	error = ext2_xattr_set(inode, name_index, "", value, size, 0);

	kfree(value);
	if (!error) {
		switch(type) {
			case ACL_TYPE_ACCESS:
				ext2_iset_acl(inode, &ei->i_acl, acl);
				break;

			case ACL_TYPE_DEFAULT:
				ext2_iset_acl(inode, &ei->i_default_acl, acl);
				break;
		}
	}
	return error;
}
Пример #30
0
static void ext2_i_callback(struct rcu_head *head)
{
	struct inode *inode = container_of(head, struct inode, i_rcu);
	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
}