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 /* Enable log-assisted xattrs. */ 50 error = xfs_add_incompat_log_feature(mp, 51 XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); 52 if (error) 53 goto drop_incompat; 54 55 xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP, 56 "EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!"); 57 58 return 0; 59 drop_incompat: 60 xlog_drop_incompat_feat(mp->m_log); 61 return error; 62 } 63 64 static inline void 65 xfs_attr_rele_log_assist( 66 struct xfs_mount *mp) 67 { 68 xlog_drop_incompat_feat(mp->m_log); 69 } 70 71 static inline bool 72 xfs_attr_want_log_assist( 73 struct xfs_mount *mp) 74 { 75 #ifdef DEBUG 76 /* Logged xattrs require a V5 super for log_incompat */ 77 return xfs_has_crc(mp) && xfs_globals.larp; 78 #else 79 return false; 80 #endif 81 } 82 83 /* 84 * Set or remove an xattr, having grabbed the appropriate logging resources 85 * prior to calling libxfs. 86 */ 87 int 88 xfs_attr_change( 89 struct xfs_da_args *args) 90 { 91 struct xfs_mount *mp = args->dp->i_mount; 92 bool use_logging = false; 93 int error; 94 95 ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED)); 96 97 if (xfs_attr_want_log_assist(mp)) { 98 error = xfs_attr_grab_log_assist(mp); 99 if (error) 100 return error; 101 102 args->op_flags |= XFS_DA_OP_LOGGED; 103 use_logging = true; 104 } 105 106 error = xfs_attr_set(args); 107 108 if (use_logging) 109 xfs_attr_rele_log_assist(mp); 110 return error; 111 } 112 113 114 static int 115 xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused, 116 struct inode *inode, const char *name, void *value, size_t size) 117 { 118 struct xfs_da_args args = { 119 .dp = XFS_I(inode), 120 .attr_filter = handler->flags, 121 .name = name, 122 .namelen = strlen(name), 123 .value = value, 124 .valuelen = size, 125 }; 126 int error; 127 128 error = xfs_attr_get(&args); 129 if (error) 130 return error; 131 return args.valuelen; 132 } 133 134 static int 135 xfs_xattr_set(const struct xattr_handler *handler, 136 struct mnt_idmap *idmap, struct dentry *unused, 137 struct inode *inode, const char *name, const void *value, 138 size_t size, int flags) 139 { 140 struct xfs_da_args args = { 141 .dp = XFS_I(inode), 142 .attr_filter = handler->flags, 143 .attr_flags = flags, 144 .name = name, 145 .namelen = strlen(name), 146 .value = (void *)value, 147 .valuelen = size, 148 }; 149 int error; 150 151 error = xfs_attr_change(&args); 152 if (!error && (handler->flags & XFS_ATTR_ROOT)) 153 xfs_forget_acl(inode, name); 154 return error; 155 } 156 157 static const struct xattr_handler xfs_xattr_user_handler = { 158 .prefix = XATTR_USER_PREFIX, 159 .flags = 0, /* no flags implies user namespace */ 160 .get = xfs_xattr_get, 161 .set = xfs_xattr_set, 162 }; 163 164 static const struct xattr_handler xfs_xattr_trusted_handler = { 165 .prefix = XATTR_TRUSTED_PREFIX, 166 .flags = XFS_ATTR_ROOT, 167 .get = xfs_xattr_get, 168 .set = xfs_xattr_set, 169 }; 170 171 static const struct xattr_handler xfs_xattr_security_handler = { 172 .prefix = XATTR_SECURITY_PREFIX, 173 .flags = XFS_ATTR_SECURE, 174 .get = xfs_xattr_get, 175 .set = xfs_xattr_set, 176 }; 177 178 const struct xattr_handler *xfs_xattr_handlers[] = { 179 &xfs_xattr_user_handler, 180 &xfs_xattr_trusted_handler, 181 &xfs_xattr_security_handler, 182 #ifdef CONFIG_XFS_POSIX_ACL 183 &posix_acl_access_xattr_handler, 184 &posix_acl_default_xattr_handler, 185 #endif 186 NULL 187 }; 188 189 static void 190 __xfs_xattr_put_listent( 191 struct xfs_attr_list_context *context, 192 char *prefix, 193 int prefix_len, 194 unsigned char *name, 195 int namelen) 196 { 197 char *offset; 198 int arraytop; 199 200 if (context->count < 0 || context->seen_enough) 201 return; 202 203 if (!context->buffer) 204 goto compute_size; 205 206 arraytop = context->count + prefix_len + namelen + 1; 207 if (arraytop > context->firstu) { 208 context->count = -1; /* insufficient space */ 209 context->seen_enough = 1; 210 return; 211 } 212 offset = context->buffer + context->count; 213 memcpy(offset, prefix, prefix_len); 214 offset += prefix_len; 215 strncpy(offset, (char *)name, namelen); /* real name */ 216 offset += namelen; 217 *offset = '\0'; 218 219 compute_size: 220 context->count += prefix_len + namelen + 1; 221 return; 222 } 223 224 static void 225 xfs_xattr_put_listent( 226 struct xfs_attr_list_context *context, 227 int flags, 228 unsigned char *name, 229 int namelen, 230 int valuelen) 231 { 232 char *prefix; 233 int prefix_len; 234 235 ASSERT(context->count >= 0); 236 237 if (flags & XFS_ATTR_ROOT) { 238 #ifdef CONFIG_XFS_POSIX_ACL 239 if (namelen == SGI_ACL_FILE_SIZE && 240 strncmp(name, SGI_ACL_FILE, 241 SGI_ACL_FILE_SIZE) == 0) { 242 __xfs_xattr_put_listent( 243 context, XATTR_SYSTEM_PREFIX, 244 XATTR_SYSTEM_PREFIX_LEN, 245 XATTR_POSIX_ACL_ACCESS, 246 strlen(XATTR_POSIX_ACL_ACCESS)); 247 } else if (namelen == SGI_ACL_DEFAULT_SIZE && 248 strncmp(name, SGI_ACL_DEFAULT, 249 SGI_ACL_DEFAULT_SIZE) == 0) { 250 __xfs_xattr_put_listent( 251 context, XATTR_SYSTEM_PREFIX, 252 XATTR_SYSTEM_PREFIX_LEN, 253 XATTR_POSIX_ACL_DEFAULT, 254 strlen(XATTR_POSIX_ACL_DEFAULT)); 255 } 256 #endif 257 258 /* 259 * Only show root namespace entries if we are actually allowed to 260 * see them. 261 */ 262 if (!capable(CAP_SYS_ADMIN)) 263 return; 264 265 prefix = XATTR_TRUSTED_PREFIX; 266 prefix_len = XATTR_TRUSTED_PREFIX_LEN; 267 } else if (flags & XFS_ATTR_SECURE) { 268 prefix = XATTR_SECURITY_PREFIX; 269 prefix_len = XATTR_SECURITY_PREFIX_LEN; 270 } else { 271 prefix = XATTR_USER_PREFIX; 272 prefix_len = XATTR_USER_PREFIX_LEN; 273 } 274 275 __xfs_xattr_put_listent(context, prefix, prefix_len, name, 276 namelen); 277 return; 278 } 279 280 ssize_t 281 xfs_vn_listxattr( 282 struct dentry *dentry, 283 char *data, 284 size_t size) 285 { 286 struct xfs_attr_list_context context; 287 struct inode *inode = d_inode(dentry); 288 int error; 289 290 /* 291 * First read the regular on-disk attributes. 292 */ 293 memset(&context, 0, sizeof(context)); 294 context.dp = XFS_I(inode); 295 context.resynch = 1; 296 context.buffer = size ? data : NULL; 297 context.bufsize = size; 298 context.firstu = context.bufsize; 299 context.put_listent = xfs_xattr_put_listent; 300 301 error = xfs_attr_list(&context); 302 if (error) 303 return error; 304 if (context.count < 0) 305 return -ERANGE; 306 307 return context.count; 308 } 309