diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 197 |
1 files changed, 188 insertions, 9 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 2624b53ea783..eff18f5b5362 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -50,7 +50,177 @@ | |||
50 | #include "volumes.h" | 50 | #include "volumes.h" |
51 | #include "locking.h" | 51 | #include "locking.h" |
52 | 52 | ||
53 | /* Mask out flags that are inappropriate for the given type of inode. */ | ||
54 | static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) | ||
55 | { | ||
56 | if (S_ISDIR(mode)) | ||
57 | return flags; | ||
58 | else if (S_ISREG(mode)) | ||
59 | return flags & ~FS_DIRSYNC_FL; | ||
60 | else | ||
61 | return flags & (FS_NODUMP_FL | FS_NOATIME_FL); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl. | ||
66 | */ | ||
67 | static unsigned int btrfs_flags_to_ioctl(unsigned int flags) | ||
68 | { | ||
69 | unsigned int iflags = 0; | ||
70 | |||
71 | if (flags & BTRFS_INODE_SYNC) | ||
72 | iflags |= FS_SYNC_FL; | ||
73 | if (flags & BTRFS_INODE_IMMUTABLE) | ||
74 | iflags |= FS_IMMUTABLE_FL; | ||
75 | if (flags & BTRFS_INODE_APPEND) | ||
76 | iflags |= FS_APPEND_FL; | ||
77 | if (flags & BTRFS_INODE_NODUMP) | ||
78 | iflags |= FS_NODUMP_FL; | ||
79 | if (flags & BTRFS_INODE_NOATIME) | ||
80 | iflags |= FS_NOATIME_FL; | ||
81 | if (flags & BTRFS_INODE_DIRSYNC) | ||
82 | iflags |= FS_DIRSYNC_FL; | ||
83 | |||
84 | return iflags; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Update inode->i_flags based on the btrfs internal flags. | ||
89 | */ | ||
90 | void btrfs_update_iflags(struct inode *inode) | ||
91 | { | ||
92 | struct btrfs_inode *ip = BTRFS_I(inode); | ||
93 | |||
94 | inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); | ||
95 | |||
96 | if (ip->flags & BTRFS_INODE_SYNC) | ||
97 | inode->i_flags |= S_SYNC; | ||
98 | if (ip->flags & BTRFS_INODE_IMMUTABLE) | ||
99 | inode->i_flags |= S_IMMUTABLE; | ||
100 | if (ip->flags & BTRFS_INODE_APPEND) | ||
101 | inode->i_flags |= S_APPEND; | ||
102 | if (ip->flags & BTRFS_INODE_NOATIME) | ||
103 | inode->i_flags |= S_NOATIME; | ||
104 | if (ip->flags & BTRFS_INODE_DIRSYNC) | ||
105 | inode->i_flags |= S_DIRSYNC; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Inherit flags from the parent inode. | ||
110 | * | ||
111 | * Unlike extN we don't have any flags we don't want to inherit currently. | ||
112 | */ | ||
113 | void btrfs_inherit_iflags(struct inode *inode, struct inode *dir) | ||
114 | { | ||
115 | unsigned int flags; | ||
116 | |||
117 | if (!dir) | ||
118 | return; | ||
119 | |||
120 | flags = BTRFS_I(dir)->flags; | ||
121 | |||
122 | if (S_ISREG(inode->i_mode)) | ||
123 | flags &= ~BTRFS_INODE_DIRSYNC; | ||
124 | else if (!S_ISDIR(inode->i_mode)) | ||
125 | flags &= (BTRFS_INODE_NODUMP | BTRFS_INODE_NOATIME); | ||
126 | |||
127 | BTRFS_I(inode)->flags = flags; | ||
128 | btrfs_update_iflags(inode); | ||
129 | } | ||
130 | |||
131 | static int btrfs_ioctl_getflags(struct file *file, void __user *arg) | ||
132 | { | ||
133 | struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode); | ||
134 | unsigned int flags = btrfs_flags_to_ioctl(ip->flags); | ||
135 | |||
136 | if (copy_to_user(arg, &flags, sizeof(flags))) | ||
137 | return -EFAULT; | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | ||
142 | { | ||
143 | struct inode *inode = file->f_path.dentry->d_inode; | ||
144 | struct btrfs_inode *ip = BTRFS_I(inode); | ||
145 | struct btrfs_root *root = ip->root; | ||
146 | struct btrfs_trans_handle *trans; | ||
147 | unsigned int flags, oldflags; | ||
148 | int ret; | ||
149 | |||
150 | if (copy_from_user(&flags, arg, sizeof(flags))) | ||
151 | return -EFAULT; | ||
152 | |||
153 | if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ | ||
154 | FS_NOATIME_FL | FS_NODUMP_FL | \ | ||
155 | FS_SYNC_FL | FS_DIRSYNC_FL)) | ||
156 | return -EOPNOTSUPP; | ||
53 | 157 | ||
158 | if (!is_owner_or_cap(inode)) | ||
159 | return -EACCES; | ||
160 | |||
161 | mutex_lock(&inode->i_mutex); | ||
162 | |||
163 | flags = btrfs_mask_flags(inode->i_mode, flags); | ||
164 | oldflags = btrfs_flags_to_ioctl(ip->flags); | ||
165 | if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { | ||
166 | if (!capable(CAP_LINUX_IMMUTABLE)) { | ||
167 | ret = -EPERM; | ||
168 | goto out_unlock; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | ret = mnt_want_write(file->f_path.mnt); | ||
173 | if (ret) | ||
174 | goto out_unlock; | ||
175 | |||
176 | if (flags & FS_SYNC_FL) | ||
177 | ip->flags |= BTRFS_INODE_SYNC; | ||
178 | else | ||
179 | ip->flags &= ~BTRFS_INODE_SYNC; | ||
180 | if (flags & FS_IMMUTABLE_FL) | ||
181 | ip->flags |= BTRFS_INODE_IMMUTABLE; | ||
182 | else | ||
183 | ip->flags &= ~BTRFS_INODE_IMMUTABLE; | ||
184 | if (flags & FS_APPEND_FL) | ||
185 | ip->flags |= BTRFS_INODE_APPEND; | ||
186 | else | ||
187 | ip->flags &= ~BTRFS_INODE_APPEND; | ||
188 | if (flags & FS_NODUMP_FL) | ||
189 | ip->flags |= BTRFS_INODE_NODUMP; | ||
190 | else | ||
191 | ip->flags &= ~BTRFS_INODE_NODUMP; | ||
192 | if (flags & FS_NOATIME_FL) | ||
193 | ip->flags |= BTRFS_INODE_NOATIME; | ||
194 | else | ||
195 | ip->flags &= ~BTRFS_INODE_NOATIME; | ||
196 | if (flags & FS_DIRSYNC_FL) | ||
197 | ip->flags |= BTRFS_INODE_DIRSYNC; | ||
198 | else | ||
199 | ip->flags &= ~BTRFS_INODE_DIRSYNC; | ||
200 | |||
201 | |||
202 | trans = btrfs_join_transaction(root, 1); | ||
203 | BUG_ON(!trans); | ||
204 | |||
205 | ret = btrfs_update_inode(trans, root, inode); | ||
206 | BUG_ON(ret); | ||
207 | |||
208 | btrfs_update_iflags(inode); | ||
209 | inode->i_ctime = CURRENT_TIME; | ||
210 | btrfs_end_transaction(trans, root); | ||
211 | |||
212 | mnt_drop_write(file->f_path.mnt); | ||
213 | out_unlock: | ||
214 | mutex_unlock(&inode->i_mutex); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int btrfs_ioctl_getversion(struct file *file, int __user *arg) | ||
219 | { | ||
220 | struct inode *inode = file->f_path.dentry->d_inode; | ||
221 | |||
222 | return put_user(inode->i_generation, arg); | ||
223 | } | ||
54 | 224 | ||
55 | static noinline int create_subvol(struct btrfs_root *root, | 225 | static noinline int create_subvol(struct btrfs_root *root, |
56 | struct dentry *dentry, | 226 | struct dentry *dentry, |
@@ -82,22 +252,25 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
82 | if (ret) | 252 | if (ret) |
83 | goto fail; | 253 | goto fail; |
84 | 254 | ||
85 | leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, | 255 | leaf = btrfs_alloc_free_block(trans, root, root->leafsize, |
86 | objectid, trans->transid, 0, 0, 0); | 256 | 0, objectid, NULL, 0, 0, 0); |
87 | if (IS_ERR(leaf)) { | 257 | if (IS_ERR(leaf)) { |
88 | ret = PTR_ERR(leaf); | 258 | ret = PTR_ERR(leaf); |
89 | goto fail; | 259 | goto fail; |
90 | } | 260 | } |
91 | 261 | ||
92 | btrfs_set_header_nritems(leaf, 0); | 262 | memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header)); |
93 | btrfs_set_header_level(leaf, 0); | ||
94 | btrfs_set_header_bytenr(leaf, leaf->start); | 263 | btrfs_set_header_bytenr(leaf, leaf->start); |
95 | btrfs_set_header_generation(leaf, trans->transid); | 264 | btrfs_set_header_generation(leaf, trans->transid); |
265 | btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); | ||
96 | btrfs_set_header_owner(leaf, objectid); | 266 | btrfs_set_header_owner(leaf, objectid); |
97 | 267 | ||
98 | write_extent_buffer(leaf, root->fs_info->fsid, | 268 | write_extent_buffer(leaf, root->fs_info->fsid, |
99 | (unsigned long)btrfs_header_fsid(leaf), | 269 | (unsigned long)btrfs_header_fsid(leaf), |
100 | BTRFS_FSID_SIZE); | 270 | BTRFS_FSID_SIZE); |
271 | write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, | ||
272 | (unsigned long)btrfs_header_chunk_tree_uuid(leaf), | ||
273 | BTRFS_UUID_SIZE); | ||
101 | btrfs_mark_buffer_dirty(leaf); | 274 | btrfs_mark_buffer_dirty(leaf); |
102 | 275 | ||
103 | inode_item = &root_item.inode; | 276 | inode_item = &root_item.inode; |
@@ -125,7 +298,7 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
125 | btrfs_set_root_dirid(&root_item, new_dirid); | 298 | btrfs_set_root_dirid(&root_item, new_dirid); |
126 | 299 | ||
127 | key.objectid = objectid; | 300 | key.objectid = objectid; |
128 | key.offset = 1; | 301 | key.offset = 0; |
129 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | 302 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
130 | ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, | 303 | ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, |
131 | &root_item); | 304 | &root_item); |
@@ -911,10 +1084,10 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, | |||
911 | if (disko) { | 1084 | if (disko) { |
912 | inode_add_bytes(inode, datal); | 1085 | inode_add_bytes(inode, datal); |
913 | ret = btrfs_inc_extent_ref(trans, root, | 1086 | ret = btrfs_inc_extent_ref(trans, root, |
914 | disko, diskl, leaf->start, | 1087 | disko, diskl, 0, |
915 | root->root_key.objectid, | 1088 | root->root_key.objectid, |
916 | trans->transid, | 1089 | inode->i_ino, |
917 | inode->i_ino); | 1090 | new_key.offset - datao); |
918 | BUG_ON(ret); | 1091 | BUG_ON(ret); |
919 | } | 1092 | } |
920 | } else if (type == BTRFS_FILE_EXTENT_INLINE) { | 1093 | } else if (type == BTRFS_FILE_EXTENT_INLINE) { |
@@ -1074,6 +1247,12 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
1074 | void __user *argp = (void __user *)arg; | 1247 | void __user *argp = (void __user *)arg; |
1075 | 1248 | ||
1076 | switch (cmd) { | 1249 | switch (cmd) { |
1250 | case FS_IOC_GETFLAGS: | ||
1251 | return btrfs_ioctl_getflags(file, argp); | ||
1252 | case FS_IOC_SETFLAGS: | ||
1253 | return btrfs_ioctl_setflags(file, argp); | ||
1254 | case FS_IOC_GETVERSION: | ||
1255 | return btrfs_ioctl_getversion(file, argp); | ||
1077 | case BTRFS_IOC_SNAP_CREATE: | 1256 | case BTRFS_IOC_SNAP_CREATE: |
1078 | return btrfs_ioctl_snap_create(file, argp, 0); | 1257 | return btrfs_ioctl_snap_create(file, argp, 0); |
1079 | case BTRFS_IOC_SUBVOL_CREATE: | 1258 | case BTRFS_IOC_SUBVOL_CREATE: |