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 | error = xfs_attr_get(&args); |
140 | if (error) |
141 | return error; |
142 | return args.valuelen; |
143 | } |
144 | |
145 | static int |
146 | xfs_xattr_set(const struct xattr_handler *handler, |
147 | struct mnt_idmap *idmap, struct dentry *unused, |
148 | struct inode *inode, const char *name, const void *value, |
149 | size_t size, int flags) |
150 | { |
151 | struct xfs_da_args args = { |
152 | .dp = XFS_I(inode), |
153 | .attr_filter = handler->flags, |
154 | .attr_flags = flags, |
155 | .name = name, |
156 | .namelen = strlen(name), |
157 | .value = (void *)value, |
158 | .valuelen = size, |
159 | }; |
160 | int error; |
161 | |
162 | error = xfs_attr_change(args: &args); |
163 | if (!error && (handler->flags & XFS_ATTR_ROOT)) |
164 | xfs_forget_acl(inode, name); |
165 | return error; |
166 | } |
167 | |
168 | static const struct xattr_handler xfs_xattr_user_handler = { |
169 | .prefix = XATTR_USER_PREFIX, |
170 | .flags = 0, /* no flags implies user namespace */ |
171 | .get = xfs_xattr_get, |
172 | .set = xfs_xattr_set, |
173 | }; |
174 | |
175 | static const struct xattr_handler xfs_xattr_trusted_handler = { |
176 | .prefix = XATTR_TRUSTED_PREFIX, |
177 | .flags = XFS_ATTR_ROOT, |
178 | .get = xfs_xattr_get, |
179 | .set = xfs_xattr_set, |
180 | }; |
181 | |
182 | static const struct xattr_handler xfs_xattr_security_handler = { |
183 | .prefix = XATTR_SECURITY_PREFIX, |
184 | .flags = XFS_ATTR_SECURE, |
185 | .get = xfs_xattr_get, |
186 | .set = xfs_xattr_set, |
187 | }; |
188 | |
189 | const struct xattr_handler * const xfs_xattr_handlers[] = { |
190 | &xfs_xattr_user_handler, |
191 | &xfs_xattr_trusted_handler, |
192 | &xfs_xattr_security_handler, |
193 | NULL |
194 | }; |
195 | |
196 | static void |
197 | __xfs_xattr_put_listent( |
198 | struct xfs_attr_list_context *context, |
199 | char *prefix, |
200 | int prefix_len, |
201 | unsigned char *name, |
202 | int namelen) |
203 | { |
204 | char *offset; |
205 | int arraytop; |
206 | |
207 | if (context->count < 0 || context->seen_enough) |
208 | return; |
209 | |
210 | if (!context->buffer) |
211 | goto compute_size; |
212 | |
213 | arraytop = context->count + prefix_len + namelen + 1; |
214 | if (arraytop > context->firstu) { |
215 | context->count = -1; /* insufficient space */ |
216 | context->seen_enough = 1; |
217 | return; |
218 | } |
219 | offset = context->buffer + context->count; |
220 | memcpy(offset, prefix, prefix_len); |
221 | offset += prefix_len; |
222 | strncpy(p: offset, q: (char *)name, size: namelen); /* real name */ |
223 | offset += namelen; |
224 | *offset = '\0'; |
225 | |
226 | compute_size: |
227 | context->count += prefix_len + namelen + 1; |
228 | return; |
229 | } |
230 | |
231 | static void |
232 | xfs_xattr_put_listent( |
233 | struct xfs_attr_list_context *context, |
234 | int flags, |
235 | unsigned char *name, |
236 | int namelen, |
237 | int valuelen) |
238 | { |
239 | char *prefix; |
240 | int prefix_len; |
241 | |
242 | ASSERT(context->count >= 0); |
243 | |
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) { |
249 | __xfs_xattr_put_listent( |
250 | context, XATTR_SYSTEM_PREFIX, |
251 | XATTR_SYSTEM_PREFIX_LEN, |
252 | XATTR_POSIX_ACL_ACCESS, |
253 | strlen(XATTR_POSIX_ACL_ACCESS)); |
254 | } else if (namelen == SGI_ACL_DEFAULT_SIZE && |
255 | strncmp(name, SGI_ACL_DEFAULT, |
256 | SGI_ACL_DEFAULT_SIZE) == 0) { |
257 | __xfs_xattr_put_listent( |
258 | context, XATTR_SYSTEM_PREFIX, |
259 | XATTR_SYSTEM_PREFIX_LEN, |
260 | XATTR_POSIX_ACL_DEFAULT, |
261 | strlen(XATTR_POSIX_ACL_DEFAULT)); |
262 | } |
263 | #endif |
264 | |
265 | /* |
266 | * Only show root namespace entries if we are actually allowed to |
267 | * see them. |
268 | */ |
269 | if (!capable(CAP_SYS_ADMIN)) |
270 | return; |
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 | |
282 | __xfs_xattr_put_listent(context, prefix, prefix_len, name, |
283 | namelen); |
284 | return; |
285 | } |
286 | |
287 | ssize_t |
288 | xfs_vn_listxattr( |
289 | struct dentry *dentry, |
290 | char *data, |
291 | size_t size) |
292 | { |
293 | struct xfs_attr_list_context context; |
294 | struct inode *inode = d_inode(dentry); |
295 | int error; |
296 | |
297 | /* |
298 | * First read the regular on-disk attributes. |
299 | */ |
300 | memset(&context, 0, sizeof(context)); |
301 | context.dp = XFS_I(inode); |
302 | context.resynch = 1; |
303 | context.buffer = size ? data : NULL; |
304 | context.bufsize = size; |
305 | context.firstu = context.bufsize; |
306 | context.put_listent = xfs_xattr_put_listent; |
307 | |
308 | error = xfs_attr_list(&context); |
309 | if (error) |
310 | return error; |
311 | if (context.count < 0) |
312 | return -ERANGE; |
313 | |
314 | return context.count; |
315 | } |
316 | |