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; } }
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; } }
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; } }
struct inode * ext2_new_inode (struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; struct buffer_head * bh2; int group, i; ino_t ino; struct inode * inode; struct ext2_group_desc * desc; struct ext2_super_block * es; int err; sb = dir->i_sb; inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); lock_super (sb); es = sb->u.ext2_sb.s_es; repeat: if (S_ISDIR(mode)) group = find_group_dir(sb, dir->u.ext2_i.i_block_group); else group = find_group_other(sb, dir->u.ext2_i.i_block_group); err = -ENOSPC; if (group == -1) goto fail; err = -EIO; bh = load_inode_bitmap (sb, group); if (IS_ERR(bh)) goto fail2; i = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, EXT2_INODES_PER_GROUP(sb)); if (i >= EXT2_INODES_PER_GROUP(sb)) goto bad_count; ext2_set_bit (i, bh->b_data); mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } ino = group * EXT2_INODES_PER_GROUP(sb) + i + 1; if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_new_inode", "reserved inode or inode > inodes count - " "block_group = %d,inode=%ld", group, ino); err = -EIO; goto fail2; } es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; inode->i_uid = current->fsuid; if (test_opt (sb, GRPID)) inode->i_gid = dir->i_gid; else if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else inode->i_gid = current->fsgid; inode->i_mode = mode; inode->i_ino = ino; inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->u.ext2_i.i_new_inode = 1; inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags & ~EXT2_BTREE_FL; if (S_ISLNK(mode)) inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL); inode->u.ext2_i.i_block_group = group; ext2_set_inode_flags(inode); insert_inode_hash(inode); inode->i_generation = event++; mark_inode_dirty(inode); #ifdef CONFIG_EXT2_FS_XATTR init_rwsem(&inode->u.ext2_i.xattr_sem); #endif unlock_super (sb); if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); err = -EDQUOT; goto fail3; } err = ext2_init_acl(inode, dir); if (err) { DQUOT_FREE_INODE(inode); goto fail3; } ext2_debug ("allocating inode %lu\n", inode->i_ino); return inode; fail3: inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); return ERR_PTR(err); fail2: desc = ext2_get_group_desc (sb, group, &bh2); desc->bg_free_inodes_count = cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) + 1); if (S_ISDIR(mode)) desc->bg_used_dirs_count = cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) - 1); mark_buffer_dirty(bh2); fail: unlock_super(sb); make_bad_inode(inode); iput(inode); return ERR_PTR(err); bad_count: ext2_error (sb, "ext2_new_inode", "Free inodes count corrupted in group %d", group); /* Is it really ENOSPC? */ err = -ENOSPC; if (sb->s_flags & MS_RDONLY) goto fail; desc = ext2_get_group_desc (sb, group, &bh2); desc->bg_free_inodes_count = 0; mark_buffer_dirty(bh2); goto repeat; }