diff options
author | Christoph Hellwig <hch@lst.de> | 2009-04-17 04:37:41 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-06-10 11:29:52 -0400 |
commit | 6cbff00f4632c8060b06bfc9585805217f11e12e (patch) | |
tree | 1886c4c855662172b84be2bfbd2aa5ac6a5c429d /fs/btrfs/ioctl.c | |
parent | c289811cc096c57ff35550ee8132793a4f9b5b59 (diff) |
Btrfs: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION
Add support for the standard attributes set via chattr and read via
lsattr. Currently we store the attributes in the flags value in
the btrfs inode, but I wonder whether we should split it into two so
that we don't have to keep converting between the two formats.
Remove the btrfs_clear_flag/btrfs_set_flag/btrfs_test_flag macros
as they were confusing the existing code and got in the way of the
new additions.
Also add the FS_IOC_GETVERSION ioctl for getting i_generation as it's
trivial.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 54dfd45cc591..926332a73cde 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -50,7 +50,172 @@ | |||
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 = BTRFS_I(dir)->flags; | ||
116 | |||
117 | if (S_ISREG(inode->i_mode)) | ||
118 | flags &= ~BTRFS_INODE_DIRSYNC; | ||
119 | else if (!S_ISDIR(inode->i_mode)) | ||
120 | flags &= (BTRFS_INODE_NODUMP | BTRFS_INODE_NOATIME); | ||
121 | |||
122 | BTRFS_I(inode)->flags = flags; | ||
123 | btrfs_update_iflags(inode); | ||
124 | } | ||
125 | |||
126 | static int btrfs_ioctl_getflags(struct file *file, void __user *arg) | ||
127 | { | ||
128 | struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode); | ||
129 | unsigned int flags = btrfs_flags_to_ioctl(ip->flags); | ||
130 | |||
131 | if (copy_to_user(arg, &flags, sizeof(flags))) | ||
132 | return -EFAULT; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | ||
137 | { | ||
138 | struct inode *inode = file->f_path.dentry->d_inode; | ||
139 | struct btrfs_inode *ip = BTRFS_I(inode); | ||
140 | struct btrfs_root *root = ip->root; | ||
141 | struct btrfs_trans_handle *trans; | ||
142 | unsigned int flags, oldflags; | ||
143 | int ret; | ||
144 | |||
145 | if (copy_from_user(&flags, arg, sizeof(flags))) | ||
146 | return -EFAULT; | ||
147 | |||
148 | if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ | ||
149 | FS_NOATIME_FL | FS_NODUMP_FL | \ | ||
150 | FS_SYNC_FL | FS_DIRSYNC_FL)) | ||
151 | return -EOPNOTSUPP; | ||
53 | 152 | ||
153 | if (!is_owner_or_cap(inode)) | ||
154 | return -EACCES; | ||
155 | |||
156 | mutex_lock(&inode->i_mutex); | ||
157 | |||
158 | flags = btrfs_mask_flags(inode->i_mode, flags); | ||
159 | oldflags = btrfs_flags_to_ioctl(ip->flags); | ||
160 | if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { | ||
161 | if (!capable(CAP_LINUX_IMMUTABLE)) { | ||
162 | ret = -EPERM; | ||
163 | goto out_unlock; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | ret = mnt_want_write(file->f_path.mnt); | ||
168 | if (ret) | ||
169 | goto out_unlock; | ||
170 | |||
171 | if (flags & FS_SYNC_FL) | ||
172 | ip->flags |= BTRFS_INODE_SYNC; | ||
173 | else | ||
174 | ip->flags &= ~BTRFS_INODE_SYNC; | ||
175 | if (flags & FS_IMMUTABLE_FL) | ||
176 | ip->flags |= BTRFS_INODE_IMMUTABLE; | ||
177 | else | ||
178 | ip->flags &= ~BTRFS_INODE_IMMUTABLE; | ||
179 | if (flags & FS_APPEND_FL) | ||
180 | ip->flags |= BTRFS_INODE_APPEND; | ||
181 | else | ||
182 | ip->flags &= ~BTRFS_INODE_APPEND; | ||
183 | if (flags & FS_NODUMP_FL) | ||
184 | ip->flags |= BTRFS_INODE_NODUMP; | ||
185 | else | ||
186 | ip->flags &= ~BTRFS_INODE_NODUMP; | ||
187 | if (flags & FS_NOATIME_FL) | ||
188 | ip->flags |= BTRFS_INODE_NOATIME; | ||
189 | else | ||
190 | ip->flags &= ~BTRFS_INODE_NOATIME; | ||
191 | if (flags & FS_DIRSYNC_FL) | ||
192 | ip->flags |= BTRFS_INODE_DIRSYNC; | ||
193 | else | ||
194 | ip->flags &= ~BTRFS_INODE_DIRSYNC; | ||
195 | |||
196 | |||
197 | trans = btrfs_join_transaction(root, 1); | ||
198 | BUG_ON(!trans); | ||
199 | |||
200 | ret = btrfs_update_inode(trans, root, inode); | ||
201 | BUG_ON(ret); | ||
202 | |||
203 | btrfs_update_iflags(inode); | ||
204 | inode->i_ctime = CURRENT_TIME; | ||
205 | btrfs_end_transaction(trans, root); | ||
206 | |||
207 | mnt_drop_write(file->f_path.mnt); | ||
208 | out_unlock: | ||
209 | mutex_unlock(&inode->i_mutex); | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int btrfs_ioctl_getversion(struct file *file, int __user *arg) | ||
214 | { | ||
215 | struct inode *inode = file->f_path.dentry->d_inode; | ||
216 | |||
217 | return put_user(inode->i_generation, arg); | ||
218 | } | ||
54 | 219 | ||
55 | static noinline int create_subvol(struct btrfs_root *root, | 220 | static noinline int create_subvol(struct btrfs_root *root, |
56 | struct dentry *dentry, | 221 | struct dentry *dentry, |
@@ -1077,6 +1242,12 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
1077 | void __user *argp = (void __user *)arg; | 1242 | void __user *argp = (void __user *)arg; |
1078 | 1243 | ||
1079 | switch (cmd) { | 1244 | switch (cmd) { |
1245 | case FS_IOC_GETFLAGS: | ||
1246 | return btrfs_ioctl_getflags(file, argp); | ||
1247 | case FS_IOC_SETFLAGS: | ||
1248 | return btrfs_ioctl_setflags(file, argp); | ||
1249 | case FS_IOC_GETVERSION: | ||
1250 | return btrfs_ioctl_getversion(file, argp); | ||
1080 | case BTRFS_IOC_SNAP_CREATE: | 1251 | case BTRFS_IOC_SNAP_CREATE: |
1081 | return btrfs_ioctl_snap_create(file, argp, 0); | 1252 | return btrfs_ioctl_snap_create(file, argp, 0); |
1082 | case BTRFS_IOC_SUBVOL_CREATE: | 1253 | case BTRFS_IOC_SUBVOL_CREATE: |