Commit | Line | Data |
---|---|---|
0b61f8a4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
f9e09f09 LM |
2 | /* |
3 | * Copyright (C) 2008 Christoph Hellwig. | |
4 | * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc. | |
f9e09f09 LM |
5 | */ |
6 | ||
7 | #include "xfs.h" | |
5467b34b | 8 | #include "xfs_shared.h" |
a4fbe6ab | 9 | #include "xfs_format.h" |
69432832 | 10 | #include "xfs_log_format.h" |
57062787 | 11 | #include "xfs_da_format.h" |
383e32b0 DW |
12 | #include "xfs_trans_resv.h" |
13 | #include "xfs_mount.h" | |
f9e09f09 | 14 | #include "xfs_inode.h" |
e0c41089 | 15 | #include "xfs_da_btree.h" |
f9e09f09 | 16 | #include "xfs_attr.h" |
5f213ddb | 17 | #include "xfs_acl.h" |
d9c61ccb DW |
18 | #include "xfs_log.h" |
19 | #include "xfs_xattr.h" | |
f9e09f09 LM |
20 | |
21 | #include <linux/posix_acl_xattr.h> | |
f9e09f09 | 22 | |
d9c61ccb DW |
23 | /* |
24 | * Get permission to use log-assisted atomic exchange of file extents. | |
25 | * | |
26 | * Callers must not be running any transactions or hold any inode locks, and | |
27 | * they must release the permission by calling xlog_drop_incompat_feat | |
28 | * when they're done. | |
29 | */ | |
efc2efeb | 30 | static inline int |
d9c61ccb DW |
31 | xfs_attr_grab_log_assist( |
32 | struct xfs_mount *mp) | |
33 | { | |
34 | int error = 0; | |
35 | ||
36 | /* | |
37 | * Protect ourselves from an idle log clearing the logged xattrs log | |
38 | * incompat feature bit. | |
39 | */ | |
40 | xlog_use_incompat_feat(mp->m_log); | |
41 | ||
42 | /* | |
43 | * If log-assisted xattrs are already enabled, the caller can use the | |
44 | * log assisted swap functions with the log-incompat reference we got. | |
45 | */ | |
46 | if (xfs_sb_version_haslogxattrs(&mp->m_sb)) | |
47 | return 0; | |
48 | ||
34389616 DW |
49 | /* |
50 | * Check if the filesystem featureset is new enough to set this log | |
51 | * incompat feature bit. Strictly speaking, the minimum requirement is | |
52 | * a V5 filesystem for the superblock field, but we'll require rmap | |
53 | * or reflink to avoid having to deal with really old kernels. | |
54 | */ | |
55 | if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp)) { | |
56 | error = -EOPNOTSUPP; | |
57 | goto drop_incompat; | |
58 | } | |
59 | ||
d9c61ccb DW |
60 | /* Enable log-assisted xattrs. */ |
61 | error = xfs_add_incompat_log_feature(mp, | |
62 | XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); | |
63 | if (error) | |
64 | goto drop_incompat; | |
65 | ||
66 | xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP, | |
67 | "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!"); | |
68 | ||
69 | return 0; | |
70 | drop_incompat: | |
71 | xlog_drop_incompat_feat(mp->m_log); | |
72 | return error; | |
73 | } | |
74 | ||
efc2efeb | 75 | static inline void |
d9c61ccb DW |
76 | xfs_attr_rele_log_assist( |
77 | struct xfs_mount *mp) | |
78 | { | |
79 | xlog_drop_incompat_feat(mp->m_log); | |
80 | } | |
f9e09f09 | 81 | |
f4288f01 DW |
82 | static inline bool |
83 | xfs_attr_want_log_assist( | |
84 | struct xfs_mount *mp) | |
85 | { | |
86 | #ifdef DEBUG | |
87 | /* Logged xattrs require a V5 super for log_incompat */ | |
88 | return xfs_has_crc(mp) && xfs_globals.larp; | |
89 | #else | |
90 | return false; | |
91 | #endif | |
92 | } | |
93 | ||
efc2efeb DW |
94 | /* |
95 | * Set or remove an xattr, having grabbed the appropriate logging resources | |
96 | * prior to calling libxfs. | |
97 | */ | |
98 | int | |
99 | xfs_attr_change( | |
100 | struct xfs_da_args *args) | |
101 | { | |
102 | struct xfs_mount *mp = args->dp->i_mount; | |
103 | bool use_logging = false; | |
104 | int error; | |
105 | ||
f4288f01 DW |
106 | ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED)); |
107 | ||
108 | if (xfs_attr_want_log_assist(mp)) { | |
efc2efeb DW |
109 | error = xfs_attr_grab_log_assist(mp); |
110 | if (error) | |
111 | return error; | |
112 | ||
f4288f01 | 113 | args->op_flags |= XFS_DA_OP_LOGGED; |
efc2efeb DW |
114 | use_logging = true; |
115 | } | |
116 | ||
117 | error = xfs_attr_set(args); | |
118 | ||
119 | if (use_logging) | |
120 | xfs_attr_rele_log_assist(mp); | |
121 | return error; | |
122 | } | |
123 | ||
124 | ||
f9e09f09 | 125 | static int |
b296821a AV |
126 | xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, |
127 | struct inode *inode, const char *name, void *value, size_t size) | |
f9e09f09 | 128 | { |
e5171d7e CH |
129 | struct xfs_da_args args = { |
130 | .dp = XFS_I(inode), | |
d5f0f49a | 131 | .attr_filter = handler->flags, |
e5171d7e CH |
132 | .name = name, |
133 | .namelen = strlen(name), | |
134 | .value = value, | |
135 | .valuelen = size, | |
136 | }; | |
137 | int error; | |
f9e09f09 | 138 | |
e5171d7e | 139 | error = xfs_attr_get(&args); |
f9e09f09 LM |
140 | if (error) |
141 | return error; | |
e5171d7e | 142 | return args.valuelen; |
f9e09f09 LM |
143 | } |
144 | ||
145 | static int | |
e65ce2a5 | 146 | xfs_xattr_set(const struct xattr_handler *handler, |
39f60c1c | 147 | struct mnt_idmap *idmap, struct dentry *unused, |
e65ce2a5 CB |
148 | struct inode *inode, const char *name, const void *value, |
149 | size_t size, int flags) | |
f9e09f09 | 150 | { |
a2544622 CH |
151 | struct xfs_da_args args = { |
152 | .dp = XFS_I(inode), | |
d5f0f49a CH |
153 | .attr_filter = handler->flags, |
154 | .attr_flags = flags, | |
a2544622 CH |
155 | .name = name, |
156 | .namelen = strlen(name), | |
157 | .value = (void *)value, | |
158 | .valuelen = size, | |
159 | }; | |
67d8e04e | 160 | int error; |
f9e09f09 | 161 | |
efc2efeb | 162 | error = xfs_attr_change(&args); |
d5f0f49a | 163 | if (!error && (handler->flags & XFS_ATTR_ROOT)) |
5a3930e2 | 164 | xfs_forget_acl(inode, name); |
67d8e04e | 165 | return error; |
f9e09f09 LM |
166 | } |
167 | ||
46e58764 | 168 | static const struct xattr_handler xfs_xattr_user_handler = { |
f9e09f09 | 169 | .prefix = XATTR_USER_PREFIX, |
431547b3 CH |
170 | .flags = 0, /* no flags implies user namespace */ |
171 | .get = xfs_xattr_get, | |
172 | .set = xfs_xattr_set, | |
f9e09f09 LM |
173 | }; |
174 | ||
46e58764 | 175 | static const struct xattr_handler xfs_xattr_trusted_handler = { |
f9e09f09 | 176 | .prefix = XATTR_TRUSTED_PREFIX, |
d5f0f49a | 177 | .flags = XFS_ATTR_ROOT, |
431547b3 CH |
178 | .get = xfs_xattr_get, |
179 | .set = xfs_xattr_set, | |
f9e09f09 LM |
180 | }; |
181 | ||
46e58764 | 182 | static const struct xattr_handler xfs_xattr_security_handler = { |
f9e09f09 | 183 | .prefix = XATTR_SECURITY_PREFIX, |
d5f0f49a | 184 | .flags = XFS_ATTR_SECURE, |
431547b3 CH |
185 | .get = xfs_xattr_get, |
186 | .set = xfs_xattr_set, | |
f9e09f09 LM |
187 | }; |
188 | ||
46e58764 | 189 | const struct xattr_handler *xfs_xattr_handlers[] = { |
f9e09f09 LM |
190 | &xfs_xattr_user_handler, |
191 | &xfs_xattr_trusted_handler, | |
192 | &xfs_xattr_security_handler, | |
f9e09f09 LM |
193 | NULL |
194 | }; | |
195 | ||
f7a136ae | 196 | static void |
5d92b75c | 197 | __xfs_xattr_put_listent( |
a9273ca5 | 198 | struct xfs_attr_list_context *context, |
5d92b75c AG |
199 | char *prefix, |
200 | int prefix_len, | |
201 | unsigned char *name, | |
202 | int namelen) | |
f9e09f09 | 203 | { |
f9e09f09 LM |
204 | char *offset; |
205 | int arraytop; | |
206 | ||
3b50086f DW |
207 | if (context->count < 0 || context->seen_enough) |
208 | return; | |
209 | ||
a9c8c69b | 210 | if (!context->buffer) |
5d92b75c | 211 | goto compute_size; |
f9e09f09 LM |
212 | |
213 | arraytop = context->count + prefix_len + namelen + 1; | |
214 | if (arraytop > context->firstu) { | |
215 | context->count = -1; /* insufficient space */ | |
791cc43b | 216 | context->seen_enough = 1; |
f7a136ae | 217 | return; |
f9e09f09 | 218 | } |
a9c8c69b | 219 | offset = context->buffer + context->count; |
fd5beaff | 220 | memcpy(offset, prefix, prefix_len); |
f9e09f09 | 221 | offset += prefix_len; |
a9273ca5 | 222 | strncpy(offset, (char *)name, namelen); /* real name */ |
f9e09f09 LM |
223 | offset += namelen; |
224 | *offset = '\0'; | |
5d92b75c AG |
225 | |
226 | compute_size: | |
f9e09f09 | 227 | context->count += prefix_len + namelen + 1; |
f7a136ae | 228 | return; |
f9e09f09 LM |
229 | } |
230 | ||
f7a136ae | 231 | static void |
5d92b75c | 232 | xfs_xattr_put_listent( |
a9273ca5 DC |
233 | struct xfs_attr_list_context *context, |
234 | int flags, | |
235 | unsigned char *name, | |
236 | int namelen, | |
e5bd12bf | 237 | int valuelen) |
f9e09f09 | 238 | { |
5d92b75c AG |
239 | char *prefix; |
240 | int prefix_len; | |
f9e09f09 | 241 | |
5d92b75c | 242 | ASSERT(context->count >= 0); |
f9e09f09 | 243 | |
5d92b75c AG |
244 | if (flags & XFS_ATTR_ROOT) { |
245 | #ifdef CONFIG_XFS_POSIX_ACL | |
246 | if (namelen == SGI_ACL_FILE_SIZE && | |
247 | strncmp(name, SGI_ACL_FILE, | |
248 | SGI_ACL_FILE_SIZE) == 0) { | |
f7a136ae | 249 | __xfs_xattr_put_listent( |
5d92b75c AG |
250 | context, XATTR_SYSTEM_PREFIX, |
251 | XATTR_SYSTEM_PREFIX_LEN, | |
252 | XATTR_POSIX_ACL_ACCESS, | |
253 | strlen(XATTR_POSIX_ACL_ACCESS)); | |
5d92b75c AG |
254 | } else if (namelen == SGI_ACL_DEFAULT_SIZE && |
255 | strncmp(name, SGI_ACL_DEFAULT, | |
256 | SGI_ACL_DEFAULT_SIZE) == 0) { | |
f7a136ae | 257 | __xfs_xattr_put_listent( |
5d92b75c AG |
258 | context, XATTR_SYSTEM_PREFIX, |
259 | XATTR_SYSTEM_PREFIX_LEN, | |
260 | XATTR_POSIX_ACL_DEFAULT, | |
261 | strlen(XATTR_POSIX_ACL_DEFAULT)); | |
5d92b75c AG |
262 | } |
263 | #endif | |
f9e09f09 | 264 | |
5d92b75c AG |
265 | /* |
266 | * Only show root namespace entries if we are actually allowed to | |
267 | * see them. | |
268 | */ | |
269 | if (!capable(CAP_SYS_ADMIN)) | |
f7a136ae | 270 | return; |
5d92b75c AG |
271 | |
272 | prefix = XATTR_TRUSTED_PREFIX; | |
273 | prefix_len = XATTR_TRUSTED_PREFIX_LEN; | |
274 | } else if (flags & XFS_ATTR_SECURE) { | |
275 | prefix = XATTR_SECURITY_PREFIX; | |
276 | prefix_len = XATTR_SECURITY_PREFIX_LEN; | |
277 | } else { | |
278 | prefix = XATTR_USER_PREFIX; | |
279 | prefix_len = XATTR_USER_PREFIX_LEN; | |
280 | } | |
281 | ||
f7a136ae ES |
282 | __xfs_xattr_put_listent(context, prefix, prefix_len, name, |
283 | namelen); | |
284 | return; | |
f9e09f09 LM |
285 | } |
286 | ||
287 | ssize_t | |
2a6fba6d ES |
288 | xfs_vn_listxattr( |
289 | struct dentry *dentry, | |
290 | char *data, | |
291 | size_t size) | |
f9e09f09 LM |
292 | { |
293 | struct xfs_attr_list_context context; | |
2a6fba6d ES |
294 | struct inode *inode = d_inode(dentry); |
295 | int error; | |
f9e09f09 LM |
296 | |
297 | /* | |
298 | * First read the regular on-disk attributes. | |
299 | */ | |
300 | memset(&context, 0, sizeof(context)); | |
301 | context.dp = XFS_I(inode); | |
f9e09f09 | 302 | context.resynch = 1; |
a9c8c69b | 303 | context.buffer = size ? data : NULL; |
f9e09f09 LM |
304 | context.bufsize = size; |
305 | context.firstu = context.bufsize; | |
5d92b75c | 306 | context.put_listent = xfs_xattr_put_listent; |
f9e09f09 | 307 | |
17e1dd83 | 308 | error = xfs_attr_list(&context); |
2a6fba6d ES |
309 | if (error) |
310 | return error; | |
f9e09f09 LM |
311 | if (context.count < 0) |
312 | return -ERANGE; | |
313 | ||
f9e09f09 LM |
314 | return context.count; |
315 | } |