xref: /kernel/linux/linux-5.10/fs/reiserfs/ioctl.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/capability.h>
68c2ecf20Sopenharmony_ci#include <linux/fs.h>
78c2ecf20Sopenharmony_ci#include <linux/mount.h>
88c2ecf20Sopenharmony_ci#include "reiserfs.h"
98c2ecf20Sopenharmony_ci#include <linux/time.h>
108c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
118c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
128c2ecf20Sopenharmony_ci#include <linux/compat.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * reiserfs_ioctl - handler for ioctl for inode
168c2ecf20Sopenharmony_ci * supported commands:
178c2ecf20Sopenharmony_ci *  1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect
188c2ecf20Sopenharmony_ci *                           and prevent packing file (argument arg has t
198c2ecf20Sopenharmony_ci *			      be non-zero)
208c2ecf20Sopenharmony_ci *  2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION
218c2ecf20Sopenharmony_ci *  3) That's all for a while ...
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_cilong reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
268c2ecf20Sopenharmony_ci	unsigned int flags;
278c2ecf20Sopenharmony_ci	int err = 0;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	reiserfs_write_lock(inode->i_sb);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	switch (cmd) {
328c2ecf20Sopenharmony_ci	case REISERFS_IOC_UNPACK:
338c2ecf20Sopenharmony_ci		if (S_ISREG(inode->i_mode)) {
348c2ecf20Sopenharmony_ci			if (arg)
358c2ecf20Sopenharmony_ci				err = reiserfs_unpack(inode, filp);
368c2ecf20Sopenharmony_ci		} else
378c2ecf20Sopenharmony_ci			err = -ENOTTY;
388c2ecf20Sopenharmony_ci		break;
398c2ecf20Sopenharmony_ci		/*
408c2ecf20Sopenharmony_ci		 * following two cases are taken from fs/ext2/ioctl.c by Remy
418c2ecf20Sopenharmony_ci		 * Card (card@masi.ibp.fr)
428c2ecf20Sopenharmony_ci		 */
438c2ecf20Sopenharmony_ci	case REISERFS_IOC_GETFLAGS:
448c2ecf20Sopenharmony_ci		if (!reiserfs_attrs(inode->i_sb)) {
458c2ecf20Sopenharmony_ci			err = -ENOTTY;
468c2ecf20Sopenharmony_ci			break;
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		flags = REISERFS_I(inode)->i_attrs;
508c2ecf20Sopenharmony_ci		err = put_user(flags, (int __user *)arg);
518c2ecf20Sopenharmony_ci		break;
528c2ecf20Sopenharmony_ci	case REISERFS_IOC_SETFLAGS:{
538c2ecf20Sopenharmony_ci			if (!reiserfs_attrs(inode->i_sb)) {
548c2ecf20Sopenharmony_ci				err = -ENOTTY;
558c2ecf20Sopenharmony_ci				break;
568c2ecf20Sopenharmony_ci			}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci			err = mnt_want_write_file(filp);
598c2ecf20Sopenharmony_ci			if (err)
608c2ecf20Sopenharmony_ci				break;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci			if (!inode_owner_or_capable(inode)) {
638c2ecf20Sopenharmony_ci				err = -EPERM;
648c2ecf20Sopenharmony_ci				goto setflags_out;
658c2ecf20Sopenharmony_ci			}
668c2ecf20Sopenharmony_ci			if (get_user(flags, (int __user *)arg)) {
678c2ecf20Sopenharmony_ci				err = -EFAULT;
688c2ecf20Sopenharmony_ci				goto setflags_out;
698c2ecf20Sopenharmony_ci			}
708c2ecf20Sopenharmony_ci			/*
718c2ecf20Sopenharmony_ci			 * Is it quota file? Do not allow user to mess with it
728c2ecf20Sopenharmony_ci			 */
738c2ecf20Sopenharmony_ci			if (IS_NOQUOTA(inode)) {
748c2ecf20Sopenharmony_ci				err = -EPERM;
758c2ecf20Sopenharmony_ci				goto setflags_out;
768c2ecf20Sopenharmony_ci			}
778c2ecf20Sopenharmony_ci			err = vfs_ioc_setflags_prepare(inode,
788c2ecf20Sopenharmony_ci						     REISERFS_I(inode)->i_attrs,
798c2ecf20Sopenharmony_ci						     flags);
808c2ecf20Sopenharmony_ci			if (err)
818c2ecf20Sopenharmony_ci				goto setflags_out;
828c2ecf20Sopenharmony_ci			if ((flags & REISERFS_NOTAIL_FL) &&
838c2ecf20Sopenharmony_ci			    S_ISREG(inode->i_mode)) {
848c2ecf20Sopenharmony_ci				int result;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci				result = reiserfs_unpack(inode, filp);
878c2ecf20Sopenharmony_ci				if (result) {
888c2ecf20Sopenharmony_ci					err = result;
898c2ecf20Sopenharmony_ci					goto setflags_out;
908c2ecf20Sopenharmony_ci				}
918c2ecf20Sopenharmony_ci			}
928c2ecf20Sopenharmony_ci			sd_attrs_to_i_attrs(flags, inode);
938c2ecf20Sopenharmony_ci			REISERFS_I(inode)->i_attrs = flags;
948c2ecf20Sopenharmony_ci			inode->i_ctime = current_time(inode);
958c2ecf20Sopenharmony_ci			mark_inode_dirty(inode);
968c2ecf20Sopenharmony_cisetflags_out:
978c2ecf20Sopenharmony_ci			mnt_drop_write_file(filp);
988c2ecf20Sopenharmony_ci			break;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	case REISERFS_IOC_GETVERSION:
1018c2ecf20Sopenharmony_ci		err = put_user(inode->i_generation, (int __user *)arg);
1028c2ecf20Sopenharmony_ci		break;
1038c2ecf20Sopenharmony_ci	case REISERFS_IOC_SETVERSION:
1048c2ecf20Sopenharmony_ci		if (!inode_owner_or_capable(inode)) {
1058c2ecf20Sopenharmony_ci			err = -EPERM;
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci		}
1088c2ecf20Sopenharmony_ci		err = mnt_want_write_file(filp);
1098c2ecf20Sopenharmony_ci		if (err)
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci		if (get_user(inode->i_generation, (int __user *)arg)) {
1128c2ecf20Sopenharmony_ci			err = -EFAULT;
1138c2ecf20Sopenharmony_ci			goto setversion_out;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci		inode->i_ctime = current_time(inode);
1168c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
1178c2ecf20Sopenharmony_cisetversion_out:
1188c2ecf20Sopenharmony_ci		mnt_drop_write_file(filp);
1198c2ecf20Sopenharmony_ci		break;
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci		err = -ENOTTY;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	reiserfs_write_unlock(inode->i_sb);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return err;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
1308c2ecf20Sopenharmony_cilong reiserfs_compat_ioctl(struct file *file, unsigned int cmd,
1318c2ecf20Sopenharmony_ci				unsigned long arg)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * These are just misnamed, they actually
1358c2ecf20Sopenharmony_ci	 * get/put from/to user an int
1368c2ecf20Sopenharmony_ci	 */
1378c2ecf20Sopenharmony_ci	switch (cmd) {
1388c2ecf20Sopenharmony_ci	case REISERFS_IOC32_UNPACK:
1398c2ecf20Sopenharmony_ci		cmd = REISERFS_IOC_UNPACK;
1408c2ecf20Sopenharmony_ci		break;
1418c2ecf20Sopenharmony_ci	case REISERFS_IOC32_GETFLAGS:
1428c2ecf20Sopenharmony_ci		cmd = REISERFS_IOC_GETFLAGS;
1438c2ecf20Sopenharmony_ci		break;
1448c2ecf20Sopenharmony_ci	case REISERFS_IOC32_SETFLAGS:
1458c2ecf20Sopenharmony_ci		cmd = REISERFS_IOC_SETFLAGS;
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci	case REISERFS_IOC32_GETVERSION:
1488c2ecf20Sopenharmony_ci		cmd = REISERFS_IOC_GETVERSION;
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	case REISERFS_IOC32_SETVERSION:
1518c2ecf20Sopenharmony_ci		cmd = REISERFS_IOC_SETVERSION;
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	default:
1548c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return reiserfs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci#endif
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciint reiserfs_commit_write(struct file *f, struct page *page,
1628c2ecf20Sopenharmony_ci			  unsigned from, unsigned to);
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * reiserfs_unpack
1658c2ecf20Sopenharmony_ci * Function try to convert tail from direct item into indirect.
1668c2ecf20Sopenharmony_ci * It set up nopack attribute in the REISERFS_I(inode)->nopack
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_ciint reiserfs_unpack(struct inode *inode, struct file *filp)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int retval = 0;
1718c2ecf20Sopenharmony_ci	int index;
1728c2ecf20Sopenharmony_ci	struct page *page;
1738c2ecf20Sopenharmony_ci	struct address_space *mapping;
1748c2ecf20Sopenharmony_ci	unsigned long write_from;
1758c2ecf20Sopenharmony_ci	unsigned long blocksize = inode->i_sb->s_blocksize;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (inode->i_size == 0) {
1788c2ecf20Sopenharmony_ci		REISERFS_I(inode)->i_flags |= i_nopack_mask;
1798c2ecf20Sopenharmony_ci		return 0;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	/* ioctl already done */
1828c2ecf20Sopenharmony_ci	if (REISERFS_I(inode)->i_flags & i_nopack_mask) {
1838c2ecf20Sopenharmony_ci		return 0;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* we need to make sure nobody is changing the file size beneath us */
1878c2ecf20Sopenharmony_ci	{
1888c2ecf20Sopenharmony_ci		int depth = reiserfs_write_unlock_nested(inode->i_sb);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		inode_lock(inode);
1918c2ecf20Sopenharmony_ci		reiserfs_write_lock_nested(inode->i_sb, depth);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	reiserfs_write_lock(inode->i_sb);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	write_from = inode->i_size & (blocksize - 1);
1978c2ecf20Sopenharmony_ci	/* if we are on a block boundary, we are already unpacked.  */
1988c2ecf20Sopenharmony_ci	if (write_from == 0) {
1998c2ecf20Sopenharmony_ci		REISERFS_I(inode)->i_flags |= i_nopack_mask;
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/*
2048c2ecf20Sopenharmony_ci	 * we unpack by finding the page with the tail, and calling
2058c2ecf20Sopenharmony_ci	 * __reiserfs_write_begin on that page.  This will force a
2068c2ecf20Sopenharmony_ci	 * reiserfs_get_block to unpack the tail for us.
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci	index = inode->i_size >> PAGE_SHIFT;
2098c2ecf20Sopenharmony_ci	mapping = inode->i_mapping;
2108c2ecf20Sopenharmony_ci	page = grab_cache_page(mapping, index);
2118c2ecf20Sopenharmony_ci	retval = -ENOMEM;
2128c2ecf20Sopenharmony_ci	if (!page) {
2138c2ecf20Sopenharmony_ci		goto out;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	retval = __reiserfs_write_begin(page, write_from, 0);
2168c2ecf20Sopenharmony_ci	if (retval)
2178c2ecf20Sopenharmony_ci		goto out_unlock;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* conversion can change page contents, must flush */
2208c2ecf20Sopenharmony_ci	flush_dcache_page(page);
2218c2ecf20Sopenharmony_ci	retval = reiserfs_commit_write(NULL, page, write_from, write_from);
2228c2ecf20Sopenharmony_ci	REISERFS_I(inode)->i_flags |= i_nopack_mask;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciout_unlock:
2258c2ecf20Sopenharmony_ci	unlock_page(page);
2268c2ecf20Sopenharmony_ci	put_page(page);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciout:
2298c2ecf20Sopenharmony_ci	inode_unlock(inode);
2308c2ecf20Sopenharmony_ci	reiserfs_write_unlock(inode->i_sb);
2318c2ecf20Sopenharmony_ci	return retval;
2328c2ecf20Sopenharmony_ci}
233