long pmfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct pmfs_inode *pi; struct super_block *sb = inode->i_sb; unsigned int flags; int ret; pmfs_transaction_t *trans; pi = pmfs_get_inode(sb, inode->i_ino); if (!pi) return -EACCES; switch (cmd) { case FS_IOC_GETFLAGS: flags = le32_to_cpu(pi->i_flags) & PMFS_FL_USER_VISIBLE; return put_user(flags, (int __user *)arg); case FS_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write_file(filp); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EPERM; goto flags_out; } if (get_user(flags, (int __user *)arg)) { ret = -EFAULT; goto flags_out; } mutex_lock(&inode->i_mutex); oldflags = le32_to_cpu(pi->i_flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto flags_out; } } if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; inode->i_ctime = CURRENT_TIME_SEC; /*TODO: This transaction can be avoided if we had RTM */ trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, MAX_DATA_PER_LENTRY, LE_DATA); pmfs_memunlock_inode(sb, pi); pi->i_flags = cpu_to_le32(flags); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pmfs_set_inode_flags(inode, pi); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); out: mutex_unlock(&inode->i_mutex); flags_out: mnt_drop_write_file(filp); return ret; } case FS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case FS_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; } mutex_lock(&inode->i_mutex); trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, sizeof(*pi), LE_DATA); inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; pmfs_memunlock_inode(sb, pi); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pi->i_generation = cpu_to_le32(inode->i_generation); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return ret; } default: return -ENOTTY; } }
long pmfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct address_space *mapping = filp->f_mapping; struct inode *inode = mapping->host; struct pmfs_inode *pi; struct super_block *sb = inode->i_sb; unsigned int flags; int ret; pmfs_transaction_t *trans; pi = pmfs_get_inode(sb, inode->i_ino); if (!pi) return -EACCES; switch (cmd) { case FS_IOC_GETFLAGS: flags = le32_to_cpu(pi->i_flags) & PMFS_FL_USER_VISIBLE; return put_user(flags, (int __user *)arg); case FS_IOC_SETFLAGS: { unsigned int oldflags; ret = mnt_want_write_file(filp); if (ret) return ret; if (!inode_owner_or_capable(inode)) { ret = -EPERM; goto flags_out; } if (get_user(flags, (int __user *)arg)) { ret = -EFAULT; goto flags_out; } mutex_lock(&inode->i_mutex); oldflags = le32_to_cpu(pi->i_flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); ret = -EPERM; goto flags_out; } } if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; inode->i_ctime = CURRENT_TIME_SEC; trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, MAX_DATA_PER_LENTRY, LE_DATA); pmfs_memunlock_inode(sb, pi); pi->i_flags = cpu_to_le32(flags); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pmfs_set_inode_flags(inode, pi); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); out: mutex_unlock(&inode->i_mutex); flags_out: mnt_drop_write_file(filp); return ret; } case FS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case FS_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; } mutex_lock(&inode->i_mutex); trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, sizeof(*pi), LE_DATA); inode->i_ctime = CURRENT_TIME_SEC; inode->i_generation = generation; pmfs_memunlock_inode(sb, pi); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pi->i_generation = cpu_to_le32(inode->i_generation); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); mutex_unlock(&inode->i_mutex); setversion_out: mnt_drop_write_file(filp); return ret; } case FS_PMFS_FSYNC: { struct sync_range packet; copy_from_user(&packet, (void *)arg, sizeof(struct sync_range)); pmfs_fsync(filp, packet.offset, packet.offset + packet.length, 1); return 0; } case PMFS_PRINT_TIMING: { pmfs_print_timing_stats(); return 0; } case PMFS_CLEAR_STATS: { pmfs_clear_stats(); return 0; } default: return -ENOTTY; } }
static long pmfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file->f_path.dentry->d_inode; struct super_block *sb = inode->i_sb; long ret = 0; unsigned long blocknr, blockoff; int num_blocks, blocksize_mask; struct pmfs_inode *pi; pmfs_transaction_t *trans; loff_t new_size; /* We only support the FALLOC_FL_KEEP_SIZE mode */ if (mode & ~FALLOC_FL_KEEP_SIZE) return -EOPNOTSUPP; if (S_ISDIR(inode->i_mode)) return -ENODEV; mutex_lock(&inode->i_mutex); new_size = len + offset; if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) { ret = inode_newsize_ok(inode, new_size); if (ret) goto out; } pi = pmfs_get_inode(sb, inode->i_ino); if (!pi) { ret = -EACCES; goto out; } trans = pmfs_new_transaction(sb, MAX_INODE_LENTRIES + MAX_METABLOCK_LENTRIES); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; } pmfs_add_logentry(sb, trans, pi, MAX_DATA_PER_LENTRY, LE_DATA); /* Set the block size hint */ pmfs_set_blocksize_hint(sb, pi, new_size); blocksize_mask = sb->s_blocksize - 1; blocknr = offset >> sb->s_blocksize_bits; blockoff = offset & blocksize_mask; num_blocks = (blockoff + len + blocksize_mask) >> sb->s_blocksize_bits; ret = pmfs_alloc_blocks(trans, inode, blocknr, num_blocks, true); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; pmfs_memunlock_inode(sb, pi); if (ret || (mode & FALLOC_FL_KEEP_SIZE)) { pi->i_flags |= cpu_to_le32(PMFS_EOFBLOCKS_FL); } if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) { inode->i_size = new_size; pi->i_size = cpu_to_le64(inode->i_size); } pi->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); pi->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); pmfs_memlock_inode(sb, pi); pmfs_commit_transaction(sb, trans); out: mutex_unlock(&inode->i_mutex); return ret; }