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(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 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 75 static inline void 76 xfs_attr_rele_log_assist( 77 struct xfs_mount *mp) 78 { 79 xlog_drop_incompat_feat(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); 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 *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(offset, (char *)name, 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