653 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2/*3 * Copyright (c) 2000,2002-2003,2005 Silicon Graphics, Inc.4 * All Rights Reserved.5 */6#ifndef __XFS_ATTR_H__7#define __XFS_ATTR_H__8 9struct xfs_inode;10struct xfs_da_args;11struct xfs_attr_list_context;12 13/*14 * Large attribute lists are structured around Btrees where all the data15 * elements are in the leaf nodes. Attribute names are hashed into an int,16 * then that int is used as the index into the Btree. Since the hashval17 * of an attribute name may not be unique, we may have duplicate keys.18 * The internal links in the Btree are logical block offsets into the file.19 *20 * Small attribute lists use a different format and are packed as tightly21 * as possible so as to fit into the literal area of the inode.22 */23 24/*25 * The maximum size (into the kernel or returned from the kernel) of an26 * attribute value or the buffer used for an attr_list() call. Larger27 * sizes will result in an ERANGE return code.28 */29#define ATTR_MAX_VALUELEN (64*1024) /* max length of a value */30 31/*32 * Kernel-internal version of the attrlist cursor.33 */34struct xfs_attrlist_cursor_kern {35 __u32 hashval; /* hash value of next entry to add */36 __u32 blkno; /* block containing entry (suggestion) */37 __u32 offset; /* offset in list of equal-hashvals */38 __u16 pad1; /* padding to match user-level */39 __u8 pad2; /* padding to match user-level */40 __u8 initted; /* T/F: cursor has been initialized */41};42 43 44/*========================================================================45 * Structure used to pass context around among the routines.46 *========================================================================*/47 48 49/* void; state communicated via *context */50typedef void (*put_listent_func_t)(struct xfs_attr_list_context *context,51 int flags, unsigned char *name, int namelen, void *value,52 int valuelen);53 54struct xfs_attr_list_context {55 struct xfs_trans *tp;56 struct xfs_inode *dp; /* inode */57 struct xfs_attrlist_cursor_kern cursor; /* position in list */58 void *buffer; /* output buffer */59 60 /*61 * Abort attribute list iteration if non-zero. Can be used to pass62 * error values to the xfs_attr_list caller.63 */64 int seen_enough;65 bool allow_incomplete;66 67 ssize_t count; /* num used entries */68 int dupcnt; /* count dup hashvals seen */69 int bufsize; /* total buffer size */70 int firstu; /* first used byte in buffer */71 unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE} */72 int resynch; /* T/F: resynch with cursor */73 put_listent_func_t put_listent; /* list output fmt function */74 int index; /* index into output buffer */75};76 77 78/*79 * ========================================================================80 * Structure used to pass context around among the delayed routines.81 * ========================================================================82 */83 84/*85 * Below is a state machine diagram for attr remove operations. The XFS_DAS_*86 * states indicate places where the function would return -EAGAIN, and then87 * immediately resume from after being called by the calling function. States88 * marked as a "subroutine state" indicate that they belong to a subroutine, and89 * so the calling function needs to pass them back to that subroutine to allow90 * it to finish where it left off. But they otherwise do not have a role in the91 * calling function other than just passing through.92 *93 * xfs_attr_remove_iter()94 * │95 * v96 * have attr to remove? ──n──> done97 * │98 * y99 * │100 * v101 * are we short form? ──y──> xfs_attr_shortform_remove ──> done102 * │103 * n104 * │105 * V106 * are we leaf form? ──y──> xfs_attr_leaf_removename ──> done107 * │108 * n109 * │110 * V111 * ┌── need to setup state?112 * │ │113 * n y114 * │ │115 * │ v116 * │ find attr and get state117 * │ attr has remote blks? ──n─┐118 * │ │ v119 * │ │ find and invalidate120 * │ y the remote blocks.121 * │ │ mark attr incomplete122 * │ ├────────────────┘123 * └──────────┤124 * │125 * v126 * Have remote blks to remove? ───y─────┐127 * │ ^ remove the blks128 * │ │ │129 * │ │ v130 * │ XFS_DAS_RMTBLK <─n── done?131 * │ re-enter with │132 * │ one less blk to y133 * │ remove │134 * │ V135 * │ refill the state136 * n │137 * │ v138 * │ XFS_DAS_RM_NAME139 * │ │140 * ├─────────────────────────┘141 * │142 * v143 * remove leaf and144 * update hash with145 * xfs_attr_node_remove_cleanup146 * │147 * v148 * need to149 * shrink tree? ─n─┐150 * │ │151 * y │152 * │ │153 * v │154 * join leaf │155 * │ │156 * v │157 * XFS_DAS_RM_SHRINK │158 * │ │159 * v │160 * do the shrink │161 * │ │162 * v │163 * free state <──┘164 * │165 * v166 * done167 *168 *169 * Below is a state machine diagram for attr set operations.170 *171 * It seems the challenge with understanding this system comes from trying to172 * absorb the state machine all at once, when really one should only be looking173 * at it with in the context of a single function. Once a state sensitive174 * function is called, the idea is that it "takes ownership" of the175 * state machine. It isn't concerned with the states that may have belonged to176 * it's calling parent. Only the states relevant to itself or any other177 * subroutines there in. Once a calling function hands off the state machine to178 * a subroutine, it needs to respect the simple rule that it doesn't "own" the179 * state machine anymore, and it's the responsibility of that calling function180 * to propagate the -EAGAIN back up the call stack. Upon reentry, it is181 * committed to re-calling that subroutine until it returns something other than182 * -EAGAIN. Once that subroutine signals completion (by returning anything other183 * than -EAGAIN), the calling function can resume using the state machine.184 *185 * xfs_attr_set_iter()186 * │187 * v188 * ┌─y─ has an attr fork?189 * │ |190 * │ n191 * │ |192 * │ V193 * │ add a fork194 * │ │195 * └──────────┤196 * │197 * V198 * ┌─── is shortform?199 * │ │200 * │ y201 * │ │202 * │ V203 * │ xfs_attr_set_fmt204 * │ |205 * │ V206 * │ xfs_attr_try_sf_addname207 * │ │208 * │ V209 * │ had enough ──y──> done210 * │ space?211 * n │212 * │ n213 * │ │214 * │ V215 * │ transform to leaf216 * │ │217 * │ V218 * │ hold the leaf buffer219 * │ │220 * │ V221 * │ return -EAGAIN222 * │ Re-enter in223 * │ leaf form224 * │225 * └─> release leaf buffer226 * if needed227 * │228 * V229 * ┌───n── fork has230 * │ only 1 blk?231 * │ │232 * │ y233 * │ │234 * │ v235 * │ xfs_attr_leaf_try_add()236 * │ │237 * │ v238 * │ had enough ──────────────y─────────────┐239 * │ space? │240 * │ │ │241 * │ n │242 * │ │ │243 * │ v │244 * │ return -EAGAIN │245 * │ re-enter in │246 * │ node form │247 * │ │ │248 * └──────────┤ │249 * │ │250 * V │251 * xfs_attr_node_addname_find_attr │252 * determines if this │253 * is create or rename │254 * find space to store attr │255 * │ │256 * v │257 * xfs_attr_node_addname │258 * │ │259 * v │260 * fits in a node leaf? ────n─────┐ │261 * │ ^ v │262 * │ │ single leaf node? │263 * │ │ │ │ │264 * y │ y n │265 * │ │ │ │ │266 * v │ v v │267 * update │ grow the leaf split if │268 * hashvals └── return -EAGAIN needed │269 * │ retry leaf add │ │270 * │ on reentry │ │271 * ├────────────────────────────┘ │272 * │ │273 * v │274 * need to alloc │275 * ┌─y── or flip flag? │276 * │ │ │277 * │ n │278 * │ │ │279 * │ v │280 * │ done │281 * │ │282 * │ │283 * │ XFS_DAS_FOUND_LBLK <────────────────┘284 * │ │285 * │ V286 * │ xfs_attr_leaf_addname()287 * │ │288 * │ v289 * │ ┌──first time through?290 * │ │ │291 * │ │ y292 * │ │ │293 * │ n v294 * │ │ if we have rmt blks295 * │ │ find space for them296 * │ │ │297 * │ └──────────┤298 * │ │299 * │ v300 * │ still have301 * │ ┌─n─ blks to alloc? <──┐302 * │ │ │ │303 * │ │ y │304 * │ │ │ │305 * │ │ v │306 * │ │ alloc one blk │307 * │ │ return -EAGAIN ──┘308 * │ │ re-enter with one309 * │ │ less blk to alloc310 * │ │311 * │ │312 * │ └───> set the rmt313 * │ value314 * │ │315 * │ v316 * │ was this317 * │ a rename? ──n─┐318 * │ │ │319 * │ y │320 * │ │ │321 * │ v │322 * │ flip incomplete │323 * │ flag │324 * │ │ │325 * │ v │326 * │ XFS_DAS_FLIP_LFLAG │327 * │ │ │328 * │ v │329 * │ need to remove │330 * │ old bks? ──n──┤331 * │ │ │332 * │ y │333 * │ │ │334 * │ V │335 * │ remove │336 * │ ┌───> old blks │337 * │ │ │ │338 * │ XFS_DAS_RM_LBLK │ │339 * │ ^ │ │340 * │ │ v │341 * │ └──y── more to │342 * │ remove? │343 * │ │ │344 * │ n │345 * │ │ │346 * │ v │347 * │ XFS_DAS_RD_LEAF │348 * │ │ │349 * │ v │350 * │ remove leaf │351 * │ │ │352 * │ v │353 * │ shrink to sf │354 * │ if needed │355 * │ │ │356 * │ v │357 * │ done <──────┘358 * │359 * └──────> XFS_DAS_FOUND_NBLK360 * │361 * v362 * ┌─────n── need to363 * │ alloc blks?364 * │ │365 * │ y366 * │ │367 * │ v368 * │ find space369 * │ │370 * │ v371 * │ ┌─>XFS_DAS_ALLOC_NODE372 * │ │ │373 * │ │ v374 * │ │ alloc blk375 * │ │ │376 * │ │ v377 * │ └──y── need to alloc378 * │ more blocks?379 * │ │380 * │ n381 * │ │382 * │ v383 * │ set the rmt value384 * │ │385 * │ v386 * │ was this387 * └────────> a rename? ──n─┐388 * │ │389 * y │390 * │ │391 * v │392 * flip incomplete │393 * flag │394 * │ │395 * v │396 * XFS_DAS_FLIP_NFLAG │397 * │ │398 * v │399 * need to │400 * remove blks? ─n──┤401 * │ │402 * y │403 * │ │404 * v │405 * remove │406 * ┌────────> old blks │407 * │ │ │408 * XFS_DAS_RM_NBLK │ │409 * ^ │ │410 * │ v │411 * └──────y── more to │412 * remove │413 * │ │414 * n │415 * │ │416 * v │417 * XFS_DAS_CLR_FLAG │418 * │ │419 * v │420 * clear flags │421 * │ │422 * ├──────────┘423 * │424 * v425 * done426 */427 428/*429 * Enum values for xfs_attr_intent.xattri_da_state430 *431 * These values are used by delayed attribute operations to keep track of where432 * they were before they returned -EAGAIN. A return code of -EAGAIN signals the433 * calling function to roll the transaction, and then call the subroutine to434 * finish the operation. The enum is then used by the subroutine to jump back435 * to where it was and resume executing where it left off.436 */437enum xfs_delattr_state {438 XFS_DAS_UNINIT = 0, /* No state has been set yet */439 440 /*441 * Initial sequence states. The replace setup code relies on the442 * ADD and REMOVE states for a specific format to be sequential so443 * that we can transform the initial operation to be performed444 * according to the xfs_has_larp() state easily.445 */446 XFS_DAS_SF_ADD, /* Initial sf add state */447 XFS_DAS_SF_REMOVE, /* Initial sf replace/remove state */448 449 XFS_DAS_LEAF_ADD, /* Initial leaf add state */450 XFS_DAS_LEAF_REMOVE, /* Initial leaf replace/remove state */451 452 XFS_DAS_NODE_ADD, /* Initial node add state */453 XFS_DAS_NODE_REMOVE, /* Initial node replace/remove state */454 455 /* Leaf state set/replace/remove sequence */456 XFS_DAS_LEAF_SET_RMT, /* set a remote xattr from a leaf */457 XFS_DAS_LEAF_ALLOC_RMT, /* We are allocating remote blocks */458 XFS_DAS_LEAF_REPLACE, /* Perform replace ops on a leaf */459 XFS_DAS_LEAF_REMOVE_OLD, /* Start removing old attr from leaf */460 XFS_DAS_LEAF_REMOVE_RMT, /* A rename is removing remote blocks */461 XFS_DAS_LEAF_REMOVE_ATTR, /* Remove the old attr from a leaf */462 463 /* Node state sequence, must match leaf state above */464 XFS_DAS_NODE_SET_RMT, /* set a remote xattr from a node */465 XFS_DAS_NODE_ALLOC_RMT, /* We are allocating remote blocks */466 XFS_DAS_NODE_REPLACE, /* Perform replace ops on a node */467 XFS_DAS_NODE_REMOVE_OLD, /* Start removing old attr from node */468 XFS_DAS_NODE_REMOVE_RMT, /* A rename is removing remote blocks */469 XFS_DAS_NODE_REMOVE_ATTR, /* Remove the old attr from a node */470 471 XFS_DAS_DONE, /* finished operation */472};473 474#define XFS_DAS_STRINGS \475 { XFS_DAS_UNINIT, "XFS_DAS_UNINIT" }, \476 { XFS_DAS_SF_ADD, "XFS_DAS_SF_ADD" }, \477 { XFS_DAS_SF_REMOVE, "XFS_DAS_SF_REMOVE" }, \478 { XFS_DAS_LEAF_ADD, "XFS_DAS_LEAF_ADD" }, \479 { XFS_DAS_LEAF_REMOVE, "XFS_DAS_LEAF_REMOVE" }, \480 { XFS_DAS_NODE_ADD, "XFS_DAS_NODE_ADD" }, \481 { XFS_DAS_NODE_REMOVE, "XFS_DAS_NODE_REMOVE" }, \482 { XFS_DAS_LEAF_SET_RMT, "XFS_DAS_LEAF_SET_RMT" }, \483 { XFS_DAS_LEAF_ALLOC_RMT, "XFS_DAS_LEAF_ALLOC_RMT" }, \484 { XFS_DAS_LEAF_REPLACE, "XFS_DAS_LEAF_REPLACE" }, \485 { XFS_DAS_LEAF_REMOVE_OLD, "XFS_DAS_LEAF_REMOVE_OLD" }, \486 { XFS_DAS_LEAF_REMOVE_RMT, "XFS_DAS_LEAF_REMOVE_RMT" }, \487 { XFS_DAS_LEAF_REMOVE_ATTR, "XFS_DAS_LEAF_REMOVE_ATTR" }, \488 { XFS_DAS_NODE_SET_RMT, "XFS_DAS_NODE_SET_RMT" }, \489 { XFS_DAS_NODE_ALLOC_RMT, "XFS_DAS_NODE_ALLOC_RMT" }, \490 { XFS_DAS_NODE_REPLACE, "XFS_DAS_NODE_REPLACE" }, \491 { XFS_DAS_NODE_REMOVE_OLD, "XFS_DAS_NODE_REMOVE_OLD" }, \492 { XFS_DAS_NODE_REMOVE_RMT, "XFS_DAS_NODE_REMOVE_RMT" }, \493 { XFS_DAS_NODE_REMOVE_ATTR, "XFS_DAS_NODE_REMOVE_ATTR" }, \494 { XFS_DAS_DONE, "XFS_DAS_DONE" }495 496struct xfs_attri_log_nameval;497 498/*499 * Context used for keeping track of delayed attribute operations500 */501struct xfs_attr_intent {502 /*503 * used to log this item to an intent containing a list of attrs to504 * commit later505 */506 struct list_head xattri_list;507 508 /* Used in xfs_attr_node_removename to roll through removing blocks */509 struct xfs_da_state *xattri_da_state;510 511 struct xfs_da_args *xattri_da_args;512 513 /*514 * Shared buffer containing the attr name, new name, and value so that515 * the logging code can share large memory buffers between log items.516 */517 struct xfs_attri_log_nameval *xattri_nameval;518 519 /* Used to keep track of current state of delayed operation */520 enum xfs_delattr_state xattri_dela_state;521 522 /*523 * Attr operation being performed - XFS_ATTRI_OP_FLAGS_*524 */525 unsigned int xattri_op_flags;526 527 /* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */528 xfs_dablk_t xattri_lblkno;529 int xattri_blkcnt;530 struct xfs_bmbt_irec xattri_map;531};532 533static inline unsigned int534xfs_attr_intent_op(const struct xfs_attr_intent *attr)535{536 return attr->xattri_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;537}538 539/*========================================================================540 * Function prototypes for the kernel.541 *========================================================================*/542 543/*544 * Overall external interface routines.545 */546int xfs_attr_inactive(struct xfs_inode *dp);547int xfs_attr_list_ilocked(struct xfs_attr_list_context *);548int xfs_attr_list(struct xfs_attr_list_context *);549int xfs_inode_hasattr(struct xfs_inode *ip);550bool xfs_attr_is_leaf(struct xfs_inode *ip);551int xfs_attr_get_ilocked(struct xfs_da_args *args);552int xfs_attr_get(struct xfs_da_args *args);553 554enum xfs_attr_update {555 XFS_ATTRUPDATE_REMOVE, /* remove attr */556 XFS_ATTRUPDATE_UPSERT, /* set value, replace any existing attr */557 XFS_ATTRUPDATE_CREATE, /* set value, fail if attr already exists */558 XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */559};560 561int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op, bool rsvd);562int xfs_attr_set_iter(struct xfs_attr_intent *attr);563int xfs_attr_remove_iter(struct xfs_attr_intent *attr);564bool xfs_attr_check_namespace(unsigned int attr_flags);565bool xfs_attr_namecheck(unsigned int attr_flags, const void *name,566 size_t length);567int xfs_attr_calc_size(struct xfs_da_args *args, int *local);568struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);569 570/*571 * Check to see if the attr should be upgraded from non-existent or shortform to572 * single-leaf-block attribute list.573 */574static inline bool575xfs_attr_is_shortform(576 struct xfs_inode *ip)577{578 return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL ||579 (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&580 ip->i_af.if_nextents == 0);581}582 583static inline enum xfs_delattr_state584xfs_attr_init_add_state(struct xfs_da_args *args)585{586 /*587 * When called from the completion of a attr remove to determine the588 * next state, the attribute fork may be null. This can occur only occur589 * on a pure remove, but we grab the next state before we check if a590 * replace operation is being performed. If we are called from any other591 * context, i_af is guaranteed to exist. Hence if the attr fork is592 * null, we were called from a pure remove operation and so we are done.593 */594 if (!xfs_inode_has_attr_fork(args->dp))595 return XFS_DAS_DONE;596 597 args->op_flags |= XFS_DA_OP_ADDNAME;598 if (xfs_attr_is_shortform(args->dp))599 return XFS_DAS_SF_ADD;600 if (xfs_attr_is_leaf(args->dp))601 return XFS_DAS_LEAF_ADD;602 return XFS_DAS_NODE_ADD;603}604 605static inline enum xfs_delattr_state606xfs_attr_init_remove_state(struct xfs_da_args *args)607{608 if (xfs_attr_is_shortform(args->dp))609 return XFS_DAS_SF_REMOVE;610 if (xfs_attr_is_leaf(args->dp))611 return XFS_DAS_LEAF_REMOVE;612 return XFS_DAS_NODE_REMOVE;613}614 615/*616 * If we are logging the attributes, then we have to start with removal of the617 * old attribute so that there is always consistent state that we can recover618 * from if the system goes down part way through. We always log the new attr619 * value, so even when we remove the attr first we still have the information in620 * the log to finish the replace operation atomically.621 */622static inline enum xfs_delattr_state623xfs_attr_init_replace_state(struct xfs_da_args *args)624{625 args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE;626 if (args->op_flags & XFS_DA_OP_LOGGED)627 return xfs_attr_init_remove_state(args);628 return xfs_attr_init_add_state(args);629}630 631xfs_dahash_t xfs_attr_hashname(const uint8_t *name, int namelen);632 633xfs_dahash_t xfs_attr_hashval(struct xfs_mount *mp, unsigned int attr_flags,634 const uint8_t *name, int namelen, const void *value,635 int valuelen);636 637/* Set the hash value for any extended attribute from any namespace. */638static inline void xfs_attr_sethash(struct xfs_da_args *args)639{640 args->hashval = xfs_attr_hashval(args->dp->i_mount, args->attr_filter,641 args->name, args->namelen,642 args->value, args->valuelen);643}644 645extern struct kmem_cache *xfs_attr_intent_cache;646int __init xfs_attr_intent_init_cache(void);647void xfs_attr_intent_destroy_cache(void);648 649int xfs_attr_sf_totsize(struct xfs_inode *dp);650int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);651 652#endif /* __XFS_ATTR_H__ */653