xref: /openbmc/linux/fs/ocfs2/ioctl.c (revision 1fa6ac37)
1 /*
2  * linux/fs/ocfs2/ioctl.c
3  *
4  * Copyright (C) 2006 Herbert Poetzl
5  * adapted from Remy Card's ext2/ioctl.c
6  */
7 
8 #include <linux/fs.h>
9 #include <linux/mount.h>
10 #include <linux/compat.h>
11 
12 #define MLOG_MASK_PREFIX ML_INODE
13 #include <cluster/masklog.h>
14 
15 #include "ocfs2.h"
16 #include "alloc.h"
17 #include "dlmglue.h"
18 #include "file.h"
19 #include "inode.h"
20 #include "journal.h"
21 
22 #include "ocfs2_fs.h"
23 #include "ioctl.h"
24 #include "resize.h"
25 #include "refcounttree.h"
26 
27 #include <linux/ext2_fs.h>
28 
29 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
30 {
31 	int status;
32 
33 	status = ocfs2_inode_lock(inode, NULL, 0);
34 	if (status < 0) {
35 		mlog_errno(status);
36 		return status;
37 	}
38 	ocfs2_get_inode_flags(OCFS2_I(inode));
39 	*flags = OCFS2_I(inode)->ip_attr;
40 	ocfs2_inode_unlock(inode, 0);
41 
42 	mlog_exit(status);
43 	return status;
44 }
45 
46 static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
47 				unsigned mask)
48 {
49 	struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode);
50 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
51 	handle_t *handle = NULL;
52 	struct buffer_head *bh = NULL;
53 	unsigned oldflags;
54 	int status;
55 
56 	mutex_lock(&inode->i_mutex);
57 
58 	status = ocfs2_inode_lock(inode, &bh, 1);
59 	if (status < 0) {
60 		mlog_errno(status);
61 		goto bail;
62 	}
63 
64 	status = -EACCES;
65 	if (!is_owner_or_cap(inode))
66 		goto bail_unlock;
67 
68 	if (!S_ISDIR(inode->i_mode))
69 		flags &= ~OCFS2_DIRSYNC_FL;
70 
71 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
72 	if (IS_ERR(handle)) {
73 		status = PTR_ERR(handle);
74 		mlog_errno(status);
75 		goto bail_unlock;
76 	}
77 
78 	oldflags = ocfs2_inode->ip_attr;
79 	flags = flags & mask;
80 	flags |= oldflags & ~mask;
81 
82 	/*
83 	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
84 	 * the relevant capability.
85 	 */
86 	status = -EPERM;
87 	if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
88 		(OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
89 		if (!capable(CAP_LINUX_IMMUTABLE))
90 			goto bail_unlock;
91 	}
92 
93 	ocfs2_inode->ip_attr = flags;
94 	ocfs2_set_inode_flags(inode);
95 
96 	status = ocfs2_mark_inode_dirty(handle, inode, bh);
97 	if (status < 0)
98 		mlog_errno(status);
99 
100 	ocfs2_commit_trans(osb, handle);
101 bail_unlock:
102 	ocfs2_inode_unlock(inode, 1);
103 bail:
104 	mutex_unlock(&inode->i_mutex);
105 
106 	brelse(bh);
107 
108 	mlog_exit(status);
109 	return status;
110 }
111 
112 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
113 {
114 	struct inode *inode = filp->f_path.dentry->d_inode;
115 	unsigned int flags;
116 	int new_clusters;
117 	int status;
118 	struct ocfs2_space_resv sr;
119 	struct ocfs2_new_group_input input;
120 	struct reflink_arguments args;
121 	const char *old_path, *new_path;
122 	bool preserve;
123 
124 	switch (cmd) {
125 	case OCFS2_IOC_GETFLAGS:
126 		status = ocfs2_get_inode_attr(inode, &flags);
127 		if (status < 0)
128 			return status;
129 
130 		flags &= OCFS2_FL_VISIBLE;
131 		return put_user(flags, (int __user *) arg);
132 	case OCFS2_IOC_SETFLAGS:
133 		if (get_user(flags, (int __user *) arg))
134 			return -EFAULT;
135 
136 		status = mnt_want_write(filp->f_path.mnt);
137 		if (status)
138 			return status;
139 		status = ocfs2_set_inode_attr(inode, flags,
140 			OCFS2_FL_MODIFIABLE);
141 		mnt_drop_write(filp->f_path.mnt);
142 		return status;
143 	case OCFS2_IOC_RESVSP:
144 	case OCFS2_IOC_RESVSP64:
145 	case OCFS2_IOC_UNRESVSP:
146 	case OCFS2_IOC_UNRESVSP64:
147 		if (copy_from_user(&sr, (int __user *) arg, sizeof(sr)))
148 			return -EFAULT;
149 
150 		return ocfs2_change_file_space(filp, cmd, &sr);
151 	case OCFS2_IOC_GROUP_EXTEND:
152 		if (!capable(CAP_SYS_RESOURCE))
153 			return -EPERM;
154 
155 		if (get_user(new_clusters, (int __user *)arg))
156 			return -EFAULT;
157 
158 		return ocfs2_group_extend(inode, new_clusters);
159 	case OCFS2_IOC_GROUP_ADD:
160 	case OCFS2_IOC_GROUP_ADD64:
161 		if (!capable(CAP_SYS_RESOURCE))
162 			return -EPERM;
163 
164 		if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
165 			return -EFAULT;
166 
167 		return ocfs2_group_add(inode, &input);
168 	case OCFS2_IOC_REFLINK:
169 		if (copy_from_user(&args, (struct reflink_arguments *)arg,
170 				   sizeof(args)))
171 			return -EFAULT;
172 		old_path = (const char *)(unsigned long)args.old_path;
173 		new_path = (const char *)(unsigned long)args.new_path;
174 		preserve = (args.preserve != 0);
175 
176 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
177 	default:
178 		return -ENOTTY;
179 	}
180 }
181 
182 #ifdef CONFIG_COMPAT
183 long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
184 {
185 	bool preserve;
186 	struct reflink_arguments args;
187 	struct inode *inode = file->f_path.dentry->d_inode;
188 
189 	switch (cmd) {
190 	case OCFS2_IOC32_GETFLAGS:
191 		cmd = OCFS2_IOC_GETFLAGS;
192 		break;
193 	case OCFS2_IOC32_SETFLAGS:
194 		cmd = OCFS2_IOC_SETFLAGS;
195 		break;
196 	case OCFS2_IOC_RESVSP:
197 	case OCFS2_IOC_RESVSP64:
198 	case OCFS2_IOC_UNRESVSP:
199 	case OCFS2_IOC_UNRESVSP64:
200 	case OCFS2_IOC_GROUP_EXTEND:
201 	case OCFS2_IOC_GROUP_ADD:
202 	case OCFS2_IOC_GROUP_ADD64:
203 		break;
204 	case OCFS2_IOC_REFLINK:
205 		if (copy_from_user(&args, (struct reflink_arguments *)arg,
206 				   sizeof(args)))
207 			return -EFAULT;
208 		preserve = (args.preserve != 0);
209 
210 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
211 					   compat_ptr(args.new_path), preserve);
212 	default:
213 		return -ENOIOCTLCMD;
214 	}
215 
216 	return ocfs2_ioctl(file, cmd, arg);
217 }
218 #endif
219