1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2008 Christoph Hellwig. |
4 | * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc. |
5 | */ |
6 | |
7 | #include "xfs.h" |
8 | #include "xfs_shared.h" |
9 | #include "xfs_format.h" |
10 | #include "xfs_log_format.h" |
11 | #include "xfs_da_format.h" |
12 | #include "xfs_trans_resv.h" |
13 | #include "xfs_mount.h" |
14 | #include "xfs_inode.h" |
15 | #include "xfs_da_btree.h" |
16 | #include "xfs_attr.h" |
17 | #include "xfs_acl.h" |
18 | #include "xfs_log.h" |
19 | #include "xfs_xattr.h" |
20 | |
21 | #include <linux/posix_acl_xattr.h> |
22 | |
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 | */ |
30 | static inline int |
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(log: 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 | |
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 | |
60 | /* Enable log-assisted xattrs. */ |
61 | error = xfs_add_incompat_log_feature(mp, |
62 | feature: 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(log: mp->m_log); |
72 | return error; |
73 | } |
74 | |
75 | static inline void |
76 | xfs_attr_rele_log_assist( |
77 | struct xfs_mount *mp) |
78 | { |
79 | xlog_drop_incompat_feat(log: mp->m_log); |
80 | } |
81 | |
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 | |
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 | |
106 | ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED)); |
107 | |
108 | if (xfs_attr_want_log_assist(mp)) { |
109 | error = xfs_attr_grab_log_assist(mp); |
110 | if (error) |
111 | return error; |
112 | |
113 | args->op_flags |= XFS_DA_OP_LOGGED; |
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 | |
125 | static int |
126 | xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, |
127 | struct inode *inode, const char *name, void *value, size_t size) |
128 | { |
129 | struct xfs_da_args args = { |
130 | .dp = XFS_I(inode), |
131 | .attr_filter = handler->flags, |
132 | .name = name, |
133 | .namelen = strlen(name), |
134 | .value = value, |
135 | .valuelen = size, |
136 | }; |
137 | int error; |
138 | |
139 | if (xfs_ifork_zapped(ip: XFS_I(inode), whichfork: XFS_ATTR_FORK)) |
140 | return -EIO; |
141 | |
142 | error = xfs_attr_get(&args); |
143 | if (error) |
144 | return error; |
145 | return args.valuelen; |
146 | } |
147 | |
148 | static int |
149 | xfs_xattr_set(const struct xattr_handler *handler, |
150 | struct mnt_idmap *idmap, struct dentry *unused, |
151 | struct inode *inode, const char *name, const void *value, |
152 | size_t size, int flags) |
153 | { |
154 | struct xfs_da_args args = { |
155 | .dp = XFS_I(inode), |
156 | .attr_filter = handler->flags, |
157 | .attr_flags = flags, |
158 | .name = name, |
159 | .namelen = strlen(name), |
160 | .value = (void *)value, |
161 | .valuelen = size, |
162 | }; |
163 | int error; |
164 | |
165 | error = xfs_attr_change(args: &args); |
166 | if (!error && (handler->flags & XFS_ATTR_ROOT)) |
167 | xfs_forget_acl(inode, name); |
168 | return error; |
169 | } |
170 | |
171 | static const struct xattr_handler xfs_xattr_user_handler = { |
172 | .prefix = XATTR_USER_PREFIX, |
173 | .flags = 0, /* no flags implies user namespace */ |
174 | .get = xfs_xattr_get, |
175 | .set = xfs_xattr_set, |
176 | }; |
177 | |
178 | static const struct xattr_handler xfs_xattr_trusted_handler = { |
179 | .prefix = XATTR_TRUSTED_PREFIX, |
180 | .flags = XFS_ATTR_ROOT, |
181 | .get = xfs_xattr_get, |
182 | .set = xfs_xattr_set, |
183 | }; |
184 | |
185 | static const struct xattr_handler xfs_xattr_security_handler = { |
186 | .prefix = XATTR_SECURITY_PREFIX, |
187 | .flags = XFS_ATTR_SECURE, |
188 | .get = xfs_xattr_get, |
189 | .set = xfs_xattr_set, |
190 | }; |
191 | |
192 | const struct xattr_handler * const xfs_xattr_handlers[] = { |
193 | &xfs_xattr_user_handler, |
194 | &xfs_xattr_trusted_handler, |
195 | &xfs_xattr_security_handler, |
196 | NULL |
197 | }; |
198 | |
199 | static void |
200 | __xfs_xattr_put_listent( |
201 | struct xfs_attr_list_context *context, |
202 | char *prefix, |
203 | int prefix_len, |
204 | unsigned char *name, |
205 | int namelen) |
206 | { |
207 | char *offset; |
208 | int arraytop; |
209 | |
210 | if (context->count < 0 || context->seen_enough) |
211 | return; |
212 | |
213 | if (!context->buffer) |
214 | goto compute_size; |
215 | |
216 | arraytop = context->count + prefix_len + namelen + 1; |
217 | if (arraytop > context->firstu) { |
218 | context->count = -1; /* insufficient space */ |
219 | context->seen_enough = 1; |
220 | return; |
221 | } |
222 | offset = context->buffer + context->count; |
223 | memcpy(offset, prefix, prefix_len); |
224 | offset += prefix_len; |
225 | strncpy(p: offset, q: (char *)name, size: namelen); /* real name */ |
226 | offset += namelen; |
227 | *offset = '\0'; |
228 | |
229 | compute_size: |
230 | context->count += prefix_len + namelen + 1; |
231 | return; |
232 | } |
233 | |
234 | static void |
235 | xfs_xattr_put_listent( |
236 | struct xfs_attr_list_context *context, |
237 | int flags, |
238 | unsigned char *name, |
239 | int namelen, |
240 | int valuelen) |
241 | { |
242 | char *prefix; |
243 | int prefix_len; |
244 | |
245 | ASSERT(context->count >= 0); |
246 | |
247 | if (flags & XFS_ATTR_ROOT) { |
248 | #ifdef CONFIG_XFS_POSIX_ACL |
249 | if (namelen == SGI_ACL_FILE_SIZE && |
250 | strncmp(name, SGI_ACL_FILE, |
251 | SGI_ACL_FILE_SIZE) == 0) { |
252 | __xfs_xattr_put_listent( |
253 | context, XATTR_SYSTEM_PREFIX, |
254 | XATTR_SYSTEM_PREFIX_LEN, |
255 | XATTR_POSIX_ACL_ACCESS, |
256 | strlen(XATTR_POSIX_ACL_ACCESS)); |
257 | } else if (namelen == SGI_ACL_DEFAULT_SIZE && |
258 | strncmp(name, SGI_ACL_DEFAULT, |
259 | SGI_ACL_DEFAULT_SIZE) == 0) { |
260 | __xfs_xattr_put_listent( |
261 | context, XATTR_SYSTEM_PREFIX, |
262 | XATTR_SYSTEM_PREFIX_LEN, |
263 | XATTR_POSIX_ACL_DEFAULT, |
264 | strlen(XATTR_POSIX_ACL_DEFAULT)); |
265 | } |
266 | #endif |
267 | |
268 | /* |
269 | * Only show root namespace entries if we are actually allowed to |
270 | * see them. |
271 | */ |
272 | if (!capable(CAP_SYS_ADMIN)) |
273 | return; |
274 | |
275 | prefix = XATTR_TRUSTED_PREFIX; |
276 | prefix_len = XATTR_TRUSTED_PREFIX_LEN; |
277 | } else if (flags & XFS_ATTR_SECURE) { |
278 | prefix = XATTR_SECURITY_PREFIX; |
279 | prefix_len = XATTR_SECURITY_PREFIX_LEN; |
280 | } else { |
281 | prefix = XATTR_USER_PREFIX; |
282 | prefix_len = XATTR_USER_PREFIX_LEN; |
283 | } |
284 | |
285 | __xfs_xattr_put_listent(context, prefix, prefix_len, name, |
286 | namelen); |
287 | return; |
288 | } |
289 | |
290 | ssize_t |
291 | xfs_vn_listxattr( |
292 | struct dentry *dentry, |
293 | char *data, |
294 | size_t size) |
295 | { |
296 | struct xfs_attr_list_context context; |
297 | struct inode *inode = d_inode(dentry); |
298 | int error; |
299 | |
300 | if (xfs_ifork_zapped(XFS_I(inode), XFS_ATTR_FORK)) |
301 | return -EIO; |
302 | |
303 | /* |
304 | * First read the regular on-disk attributes. |
305 | */ |
306 | memset(&context, 0, sizeof(context)); |
307 | context.dp = XFS_I(inode); |
308 | context.resynch = 1; |
309 | context.buffer = size ? data : NULL; |
310 | context.bufsize = size; |
311 | context.firstu = context.bufsize; |
312 | context.put_listent = xfs_xattr_put_listent; |
313 | |
314 | error = xfs_attr_list(&context); |
315 | if (error) |
316 | return error; |
317 | if (context.count < 0) |
318 | return -ERANGE; |
319 | |
320 | return context.count; |
321 | } |
322 | |