xref: /openbmc/linux/fs/ext4/ioctl.c (revision ccd2506b)
1ac27a0ecSDave Kleikamp /*
2617ba13bSMingming Cao  * linux/fs/ext4/ioctl.c
3ac27a0ecSDave Kleikamp  *
4ac27a0ecSDave Kleikamp  * Copyright (C) 1993, 1994, 1995
5ac27a0ecSDave Kleikamp  * Remy Card (card@masi.ibp.fr)
6ac27a0ecSDave Kleikamp  * Laboratoire MASI - Institut Blaise Pascal
7ac27a0ecSDave Kleikamp  * Universite Pierre et Marie Curie (Paris VI)
8ac27a0ecSDave Kleikamp  */
9ac27a0ecSDave Kleikamp 
10ac27a0ecSDave Kleikamp #include <linux/fs.h>
11dab291afSMingming Cao #include <linux/jbd2.h>
12ac27a0ecSDave Kleikamp #include <linux/capability.h>
13ac27a0ecSDave Kleikamp #include <linux/time.h>
14ac27a0ecSDave Kleikamp #include <linux/compat.h>
15ac27a0ecSDave Kleikamp #include <linux/smp_lock.h>
1642a74f20SDave Hansen #include <linux/mount.h>
17ac27a0ecSDave Kleikamp #include <asm/uaccess.h>
183dcf5451SChristoph Hellwig #include "ext4_jbd2.h"
193dcf5451SChristoph Hellwig #include "ext4.h"
20ac27a0ecSDave Kleikamp 
215cdd7b2dSAndi Kleen long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
22ac27a0ecSDave Kleikamp {
235cdd7b2dSAndi Kleen 	struct inode *inode = filp->f_dentry->d_inode;
24617ba13bSMingming Cao 	struct ext4_inode_info *ei = EXT4_I(inode);
25ac27a0ecSDave Kleikamp 	unsigned int flags;
26ac27a0ecSDave Kleikamp 
27617ba13bSMingming Cao 	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
28ac27a0ecSDave Kleikamp 
29ac27a0ecSDave Kleikamp 	switch (cmd) {
30617ba13bSMingming Cao 	case EXT4_IOC_GETFLAGS:
31ff9ddf7eSJan Kara 		ext4_get_inode_flags(ei);
32617ba13bSMingming Cao 		flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
33ac27a0ecSDave Kleikamp 		return put_user(flags, (int __user *) arg);
34617ba13bSMingming Cao 	case EXT4_IOC_SETFLAGS: {
35ac27a0ecSDave Kleikamp 		handle_t *handle = NULL;
364db46fc2SAneesh Kumar K.V 		int err, migrate = 0;
37617ba13bSMingming Cao 		struct ext4_iloc iloc;
38ac27a0ecSDave Kleikamp 		unsigned int oldflags;
39ac27a0ecSDave Kleikamp 		unsigned int jflag;
40ac27a0ecSDave Kleikamp 
413bd858abSSatyam Sharma 		if (!is_owner_or_cap(inode))
42ac27a0ecSDave Kleikamp 			return -EACCES;
43ac27a0ecSDave Kleikamp 
44ac27a0ecSDave Kleikamp 		if (get_user(flags, (int __user *) arg))
45ac27a0ecSDave Kleikamp 			return -EFAULT;
46ac27a0ecSDave Kleikamp 
4742a74f20SDave Hansen 		err = mnt_want_write(filp->f_path.mnt);
4842a74f20SDave Hansen 		if (err)
4942a74f20SDave Hansen 			return err;
5042a74f20SDave Hansen 
512dc6b0d4SDuane Griffin 		flags = ext4_mask_flags(inode->i_mode, flags);
52ac27a0ecSDave Kleikamp 
5342a74f20SDave Hansen 		err = -EPERM;
54ac27a0ecSDave Kleikamp 		mutex_lock(&inode->i_mutex);
55e47776a0SJan Kara 		/* Is it quota file? Do not allow user to mess with it */
5642a74f20SDave Hansen 		if (IS_NOQUOTA(inode))
5742a74f20SDave Hansen 			goto flags_out;
5842a74f20SDave Hansen 
59ac27a0ecSDave Kleikamp 		oldflags = ei->i_flags;
60ac27a0ecSDave Kleikamp 
61ac27a0ecSDave Kleikamp 		/* The JOURNAL_DATA flag is modifiable only by root */
62617ba13bSMingming Cao 		jflag = flags & EXT4_JOURNAL_DATA_FL;
63ac27a0ecSDave Kleikamp 
64ac27a0ecSDave Kleikamp 		/*
65ac27a0ecSDave Kleikamp 		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
66ac27a0ecSDave Kleikamp 		 * the relevant capability.
67ac27a0ecSDave Kleikamp 		 *
68ac27a0ecSDave Kleikamp 		 * This test looks nicer. Thanks to Pauline Middelink
69ac27a0ecSDave Kleikamp 		 */
70617ba13bSMingming Cao 		if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
7142a74f20SDave Hansen 			if (!capable(CAP_LINUX_IMMUTABLE))
7242a74f20SDave Hansen 				goto flags_out;
73ac27a0ecSDave Kleikamp 		}
74ac27a0ecSDave Kleikamp 
75ac27a0ecSDave Kleikamp 		/*
76ac27a0ecSDave Kleikamp 		 * The JOURNAL_DATA flag can only be changed by
77ac27a0ecSDave Kleikamp 		 * the relevant capability.
78ac27a0ecSDave Kleikamp 		 */
79617ba13bSMingming Cao 		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
8042a74f20SDave Hansen 			if (!capable(CAP_SYS_RESOURCE))
8142a74f20SDave Hansen 				goto flags_out;
82ac27a0ecSDave Kleikamp 		}
834db46fc2SAneesh Kumar K.V 		if (oldflags & EXT4_EXTENTS_FL) {
844db46fc2SAneesh Kumar K.V 			/* We don't support clearning extent flags */
854db46fc2SAneesh Kumar K.V 			if (!(flags & EXT4_EXTENTS_FL)) {
864db46fc2SAneesh Kumar K.V 				err = -EOPNOTSUPP;
874db46fc2SAneesh Kumar K.V 				goto flags_out;
884db46fc2SAneesh Kumar K.V 			}
894db46fc2SAneesh Kumar K.V 		} else if (flags & EXT4_EXTENTS_FL) {
904db46fc2SAneesh Kumar K.V 			/* migrate the file */
914db46fc2SAneesh Kumar K.V 			migrate = 1;
924db46fc2SAneesh Kumar K.V 			flags &= ~EXT4_EXTENTS_FL;
934db46fc2SAneesh Kumar K.V 		}
94ac27a0ecSDave Kleikamp 
95617ba13bSMingming Cao 		handle = ext4_journal_start(inode, 1);
96ac27a0ecSDave Kleikamp 		if (IS_ERR(handle)) {
9742a74f20SDave Hansen 			err = PTR_ERR(handle);
9842a74f20SDave Hansen 			goto flags_out;
99ac27a0ecSDave Kleikamp 		}
100ac27a0ecSDave Kleikamp 		if (IS_SYNC(inode))
1010390131bSFrank Mayhar 			ext4_handle_sync(handle);
102617ba13bSMingming Cao 		err = ext4_reserve_inode_write(handle, inode, &iloc);
103ac27a0ecSDave Kleikamp 		if (err)
104ac27a0ecSDave Kleikamp 			goto flags_err;
105ac27a0ecSDave Kleikamp 
106617ba13bSMingming Cao 		flags = flags & EXT4_FL_USER_MODIFIABLE;
107617ba13bSMingming Cao 		flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE;
108ac27a0ecSDave Kleikamp 		ei->i_flags = flags;
109ac27a0ecSDave Kleikamp 
110617ba13bSMingming Cao 		ext4_set_inode_flags(inode);
111ef7f3835SKalpak Shah 		inode->i_ctime = ext4_current_time(inode);
112ac27a0ecSDave Kleikamp 
113617ba13bSMingming Cao 		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
114ac27a0ecSDave Kleikamp flags_err:
115617ba13bSMingming Cao 		ext4_journal_stop(handle);
11642a74f20SDave Hansen 		if (err)
11742a74f20SDave Hansen 			goto flags_out;
118ac27a0ecSDave Kleikamp 
119617ba13bSMingming Cao 		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
120617ba13bSMingming Cao 			err = ext4_change_inode_journal_flag(inode, jflag);
1214db46fc2SAneesh Kumar K.V 		if (err)
1224db46fc2SAneesh Kumar K.V 			goto flags_out;
1234db46fc2SAneesh Kumar K.V 		if (migrate)
1244db46fc2SAneesh Kumar K.V 			err = ext4_ext_migrate(inode);
12542a74f20SDave Hansen flags_out:
126ac27a0ecSDave Kleikamp 		mutex_unlock(&inode->i_mutex);
12742a74f20SDave Hansen 		mnt_drop_write(filp->f_path.mnt);
128ac27a0ecSDave Kleikamp 		return err;
129ac27a0ecSDave Kleikamp 	}
130617ba13bSMingming Cao 	case EXT4_IOC_GETVERSION:
131617ba13bSMingming Cao 	case EXT4_IOC_GETVERSION_OLD:
132ac27a0ecSDave Kleikamp 		return put_user(inode->i_generation, (int __user *) arg);
133617ba13bSMingming Cao 	case EXT4_IOC_SETVERSION:
134617ba13bSMingming Cao 	case EXT4_IOC_SETVERSION_OLD: {
135ac27a0ecSDave Kleikamp 		handle_t *handle;
136617ba13bSMingming Cao 		struct ext4_iloc iloc;
137ac27a0ecSDave Kleikamp 		__u32 generation;
138ac27a0ecSDave Kleikamp 		int err;
139ac27a0ecSDave Kleikamp 
1403bd858abSSatyam Sharma 		if (!is_owner_or_cap(inode))
141ac27a0ecSDave Kleikamp 			return -EPERM;
14242a74f20SDave Hansen 
14342a74f20SDave Hansen 		err = mnt_want_write(filp->f_path.mnt);
14442a74f20SDave Hansen 		if (err)
14542a74f20SDave Hansen 			return err;
14642a74f20SDave Hansen 		if (get_user(generation, (int __user *) arg)) {
14742a74f20SDave Hansen 			err = -EFAULT;
14842a74f20SDave Hansen 			goto setversion_out;
14942a74f20SDave Hansen 		}
150ac27a0ecSDave Kleikamp 
151617ba13bSMingming Cao 		handle = ext4_journal_start(inode, 1);
15242a74f20SDave Hansen 		if (IS_ERR(handle)) {
15342a74f20SDave Hansen 			err = PTR_ERR(handle);
15442a74f20SDave Hansen 			goto setversion_out;
15542a74f20SDave Hansen 		}
156617ba13bSMingming Cao 		err = ext4_reserve_inode_write(handle, inode, &iloc);
157ac27a0ecSDave Kleikamp 		if (err == 0) {
158ef7f3835SKalpak Shah 			inode->i_ctime = ext4_current_time(inode);
159ac27a0ecSDave Kleikamp 			inode->i_generation = generation;
160617ba13bSMingming Cao 			err = ext4_mark_iloc_dirty(handle, inode, &iloc);
161ac27a0ecSDave Kleikamp 		}
162617ba13bSMingming Cao 		ext4_journal_stop(handle);
16342a74f20SDave Hansen setversion_out:
16442a74f20SDave Hansen 		mnt_drop_write(filp->f_path.mnt);
165ac27a0ecSDave Kleikamp 		return err;
166ac27a0ecSDave Kleikamp 	}
167e23291b9SJose R. Santos #ifdef CONFIG_JBD2_DEBUG
168617ba13bSMingming Cao 	case EXT4_IOC_WAIT_FOR_READONLY:
169ac27a0ecSDave Kleikamp 		/*
170ac27a0ecSDave Kleikamp 		 * This is racy - by the time we're woken up and running,
171ac27a0ecSDave Kleikamp 		 * the superblock could be released.  And the module could
172ac27a0ecSDave Kleikamp 		 * have been unloaded.  So sue me.
173ac27a0ecSDave Kleikamp 		 *
174ac27a0ecSDave Kleikamp 		 * Returns 1 if it slept, else zero.
175ac27a0ecSDave Kleikamp 		 */
176ac27a0ecSDave Kleikamp 		{
177ac27a0ecSDave Kleikamp 			struct super_block *sb = inode->i_sb;
178ac27a0ecSDave Kleikamp 			DECLARE_WAITQUEUE(wait, current);
179ac27a0ecSDave Kleikamp 			int ret = 0;
180ac27a0ecSDave Kleikamp 
181ac27a0ecSDave Kleikamp 			set_current_state(TASK_INTERRUPTIBLE);
182617ba13bSMingming Cao 			add_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait);
183617ba13bSMingming Cao 			if (timer_pending(&EXT4_SB(sb)->turn_ro_timer)) {
184ac27a0ecSDave Kleikamp 				schedule();
185ac27a0ecSDave Kleikamp 				ret = 1;
186ac27a0ecSDave Kleikamp 			}
187617ba13bSMingming Cao 			remove_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait);
188ac27a0ecSDave Kleikamp 			return ret;
189ac27a0ecSDave Kleikamp 		}
190ac27a0ecSDave Kleikamp #endif
191617ba13bSMingming Cao 	case EXT4_IOC_GROUP_EXTEND: {
192617ba13bSMingming Cao 		ext4_fsblk_t n_blocks_count;
193ac27a0ecSDave Kleikamp 		struct super_block *sb = inode->i_sb;
1947ffe1ea8SHidehiro Kawai 		int err, err2;
195ac27a0ecSDave Kleikamp 
196ac27a0ecSDave Kleikamp 		if (!capable(CAP_SYS_RESOURCE))
197ac27a0ecSDave Kleikamp 			return -EPERM;
198ac27a0ecSDave Kleikamp 
199ac27a0ecSDave Kleikamp 		if (get_user(n_blocks_count, (__u32 __user *)arg))
200ac27a0ecSDave Kleikamp 			return -EFAULT;
201ac27a0ecSDave Kleikamp 
20242a74f20SDave Hansen 		err = mnt_want_write(filp->f_path.mnt);
20342a74f20SDave Hansen 		if (err)
20442a74f20SDave Hansen 			return err;
20542a74f20SDave Hansen 
206617ba13bSMingming Cao 		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
207dab291afSMingming Cao 		jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
2087ffe1ea8SHidehiro Kawai 		err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
209dab291afSMingming Cao 		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
2107ffe1ea8SHidehiro Kawai 		if (err == 0)
2117ffe1ea8SHidehiro Kawai 			err = err2;
21242a74f20SDave Hansen 		mnt_drop_write(filp->f_path.mnt);
213ac27a0ecSDave Kleikamp 
214ac27a0ecSDave Kleikamp 		return err;
215ac27a0ecSDave Kleikamp 	}
216617ba13bSMingming Cao 	case EXT4_IOC_GROUP_ADD: {
217617ba13bSMingming Cao 		struct ext4_new_group_data input;
218ac27a0ecSDave Kleikamp 		struct super_block *sb = inode->i_sb;
2197ffe1ea8SHidehiro Kawai 		int err, err2;
220ac27a0ecSDave Kleikamp 
221ac27a0ecSDave Kleikamp 		if (!capable(CAP_SYS_RESOURCE))
222ac27a0ecSDave Kleikamp 			return -EPERM;
223ac27a0ecSDave Kleikamp 
224617ba13bSMingming Cao 		if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
225ac27a0ecSDave Kleikamp 				sizeof(input)))
226ac27a0ecSDave Kleikamp 			return -EFAULT;
227ac27a0ecSDave Kleikamp 
22842a74f20SDave Hansen 		err = mnt_want_write(filp->f_path.mnt);
22942a74f20SDave Hansen 		if (err)
23042a74f20SDave Hansen 			return err;
23142a74f20SDave Hansen 
232617ba13bSMingming Cao 		err = ext4_group_add(sb, &input);
233dab291afSMingming Cao 		jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
2347ffe1ea8SHidehiro Kawai 		err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
235dab291afSMingming Cao 		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
2367ffe1ea8SHidehiro Kawai 		if (err == 0)
2377ffe1ea8SHidehiro Kawai 			err = err2;
23842a74f20SDave Hansen 		mnt_drop_write(filp->f_path.mnt);
239ac27a0ecSDave Kleikamp 
240ac27a0ecSDave Kleikamp 		return err;
241ac27a0ecSDave Kleikamp 	}
242ac27a0ecSDave Kleikamp 
243c14c6fd5SAneesh Kumar K.V 	case EXT4_IOC_MIGRATE:
2442a43a878SAneesh Kumar K.V 	{
2452a43a878SAneesh Kumar K.V 		int err;
2462a43a878SAneesh Kumar K.V 		if (!is_owner_or_cap(inode))
2472a43a878SAneesh Kumar K.V 			return -EACCES;
2482a43a878SAneesh Kumar K.V 
2492a43a878SAneesh Kumar K.V 		err = mnt_want_write(filp->f_path.mnt);
2502a43a878SAneesh Kumar K.V 		if (err)
2512a43a878SAneesh Kumar K.V 			return err;
2522a43a878SAneesh Kumar K.V 		/*
2532a43a878SAneesh Kumar K.V 		 * inode_mutex prevent write and truncate on the file.
2542a43a878SAneesh Kumar K.V 		 * Read still goes through. We take i_data_sem in
2552a43a878SAneesh Kumar K.V 		 * ext4_ext_swap_inode_data before we switch the
2562a43a878SAneesh Kumar K.V 		 * inode format to prevent read.
2572a43a878SAneesh Kumar K.V 		 */
2582a43a878SAneesh Kumar K.V 		mutex_lock(&(inode->i_mutex));
2592a43a878SAneesh Kumar K.V 		err = ext4_ext_migrate(inode);
2602a43a878SAneesh Kumar K.V 		mutex_unlock(&(inode->i_mutex));
2612a43a878SAneesh Kumar K.V 		mnt_drop_write(filp->f_path.mnt);
2622a43a878SAneesh Kumar K.V 		return err;
2632a43a878SAneesh Kumar K.V 	}
264c14c6fd5SAneesh Kumar K.V 
265ccd2506bSTheodore Ts'o 	case EXT4_IOC_ALLOC_DA_BLKS:
266ccd2506bSTheodore Ts'o 	{
267ccd2506bSTheodore Ts'o 		int err;
268ccd2506bSTheodore Ts'o 		if (!is_owner_or_cap(inode))
269ccd2506bSTheodore Ts'o 			return -EACCES;
270ccd2506bSTheodore Ts'o 
271ccd2506bSTheodore Ts'o 		err = mnt_want_write(filp->f_path.mnt);
272ccd2506bSTheodore Ts'o 		if (err)
273ccd2506bSTheodore Ts'o 			return err;
274ccd2506bSTheodore Ts'o 		err = ext4_alloc_da_blocks(inode);
275ccd2506bSTheodore Ts'o 		mnt_drop_write(filp->f_path.mnt);
276ccd2506bSTheodore Ts'o 		return err;
277ccd2506bSTheodore Ts'o 	}
278ccd2506bSTheodore Ts'o 
279ac27a0ecSDave Kleikamp 	default:
280ac27a0ecSDave Kleikamp 		return -ENOTTY;
281ac27a0ecSDave Kleikamp 	}
282ac27a0ecSDave Kleikamp }
283ac27a0ecSDave Kleikamp 
284ac27a0ecSDave Kleikamp #ifdef CONFIG_COMPAT
285617ba13bSMingming Cao long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
286ac27a0ecSDave Kleikamp {
287ac27a0ecSDave Kleikamp 	/* These are just misnamed, they actually get/put from/to user an int */
288ac27a0ecSDave Kleikamp 	switch (cmd) {
289617ba13bSMingming Cao 	case EXT4_IOC32_GETFLAGS:
290617ba13bSMingming Cao 		cmd = EXT4_IOC_GETFLAGS;
291ac27a0ecSDave Kleikamp 		break;
292617ba13bSMingming Cao 	case EXT4_IOC32_SETFLAGS:
293617ba13bSMingming Cao 		cmd = EXT4_IOC_SETFLAGS;
294ac27a0ecSDave Kleikamp 		break;
295617ba13bSMingming Cao 	case EXT4_IOC32_GETVERSION:
296617ba13bSMingming Cao 		cmd = EXT4_IOC_GETVERSION;
297ac27a0ecSDave Kleikamp 		break;
298617ba13bSMingming Cao 	case EXT4_IOC32_SETVERSION:
299617ba13bSMingming Cao 		cmd = EXT4_IOC_SETVERSION;
300ac27a0ecSDave Kleikamp 		break;
301617ba13bSMingming Cao 	case EXT4_IOC32_GROUP_EXTEND:
302617ba13bSMingming Cao 		cmd = EXT4_IOC_GROUP_EXTEND;
303ac27a0ecSDave Kleikamp 		break;
304617ba13bSMingming Cao 	case EXT4_IOC32_GETVERSION_OLD:
305617ba13bSMingming Cao 		cmd = EXT4_IOC_GETVERSION_OLD;
306ac27a0ecSDave Kleikamp 		break;
307617ba13bSMingming Cao 	case EXT4_IOC32_SETVERSION_OLD:
308617ba13bSMingming Cao 		cmd = EXT4_IOC_SETVERSION_OLD;
309ac27a0ecSDave Kleikamp 		break;
310e23291b9SJose R. Santos #ifdef CONFIG_JBD2_DEBUG
311617ba13bSMingming Cao 	case EXT4_IOC32_WAIT_FOR_READONLY:
312617ba13bSMingming Cao 		cmd = EXT4_IOC_WAIT_FOR_READONLY;
313ac27a0ecSDave Kleikamp 		break;
314ac27a0ecSDave Kleikamp #endif
315617ba13bSMingming Cao 	case EXT4_IOC32_GETRSVSZ:
316617ba13bSMingming Cao 		cmd = EXT4_IOC_GETRSVSZ;
317ac27a0ecSDave Kleikamp 		break;
318617ba13bSMingming Cao 	case EXT4_IOC32_SETRSVSZ:
319617ba13bSMingming Cao 		cmd = EXT4_IOC_SETRSVSZ;
320ac27a0ecSDave Kleikamp 		break;
321617ba13bSMingming Cao 	case EXT4_IOC_GROUP_ADD:
322ac27a0ecSDave Kleikamp 		break;
323ac27a0ecSDave Kleikamp 	default:
324ac27a0ecSDave Kleikamp 		return -ENOIOCTLCMD;
325ac27a0ecSDave Kleikamp 	}
3265cdd7b2dSAndi Kleen 	return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
327ac27a0ecSDave Kleikamp }
328ac27a0ecSDave Kleikamp #endif
329