]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_attr_leaf.c
Support for updated inline extended attributes format (attr2).
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_attr_leaf.c
index 7140bcd56c52842d84949e52e0c63d3af4919397..5c5efefba76737d896533eb244fe02659384f545 100644 (file)
  */
 
 /*========================================================================
- * External routines when dirsize < XFS_LITINO(mp).
+ * External routines when attributes < XFS_LITINO(mp).
  *========================================================================*/
 
 /*
- * Create the initial contents of a shortform attribute list.
+ * Query whether additional requested number of bytes of extended attribute
+ * space will be able to fit inline.
+ * Returns zero if not, else the di_forkoff fork offset to be used in the
+ * literal area for attribute data once the new bytes have been added.
  */
 int
+xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
+{
+       int offset, minforkoff, maxforkoff;
+
+       offset = (XFS_LITINO(dp->i_mount) - bytes) >> 3; /* rounded down */
+
+       switch (dp->i_d.di_format) {
+       case XFS_DINODE_FMT_DEV:
+               minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
+               return (offset >= minforkoff) ? minforkoff : 0;
+       case XFS_DINODE_FMT_UUID:
+               minforkoff = roundup(sizeof(uuid_t), 8) >> 3;
+               return (offset >= minforkoff) ? minforkoff : 0;
+       }
+
+       /* data fork btree root can have at least this many key/ptr pairs */
+       minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
+       minforkoff = roundup(minforkoff, 8) >> 3;
+
+       /* attr fork btree root can have at least this many key/ptr pairs */
+       maxforkoff = XFS_LITINO(dp->i_mount) - XFS_BMDR_SPACE_CALC(MINABTPTRS);
+       maxforkoff = maxforkoff >> 3;   /* rounded down */
+
+       if (offset >= minforkoff && offset < maxforkoff)
+               return offset;
+       if (offset >= maxforkoff)
+               return maxforkoff;
+       return 0;
+}
+
+/*
+ * Create the initial contents of a shortform attribute list.
+ */
+void
 xfs_attr_shortform_create(xfs_da_args_t *args)
 {
        xfs_attr_sf_hdr_t *hdr;
@@ -69,29 +106,38 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
        hdr->count = 0;
        INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
        xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
-       return(0);
 }
 
 /*
  * Add a name/value pair to the shortform attribute list.
  * Overflow from the inode has already been checked for.
  */
-int
-xfs_attr_shortform_add(xfs_da_args_t *args)
+void
+xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
 {
        xfs_attr_shortform_t *sf;
        xfs_attr_sf_entry_t *sfe;
        int i, offset, size;
+       unsigned long s;
+       xfs_mount_t *mp;
        xfs_inode_t *dp;
        xfs_ifork_t *ifp;
 
        dp = args->dp;
+       mp = dp->i_mount;
+       dp->i_d.di_forkoff = forkoff;
+       dp->i_df.if_ext_max =
+               XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+       dp->i_afp->if_ext_max =
+               XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+
        ifp = dp->i_afp;
        ASSERT(ifp->if_flags & XFS_IFINLINE);
        sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
        sfe = &sf->list[0];
        for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
                                sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+#ifdef DEBUG
                if (sfe->namelen != args->namelen)
                        continue;
                if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
@@ -102,7 +148,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
                if (((args->flags & ATTR_ROOT) != 0) !=
                    ((sfe->flags & XFS_ATTR_ROOT) != 0))
                        continue;
-               return(XFS_ERROR(EEXIST));
+               ASSERT(0);
+#endif
        }
 
        offset = (char *)sfe - (char *)sf;
@@ -121,11 +168,20 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
        INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
        xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
 
-       return(0);
+       if (unlikely(!XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
+               s = XFS_SB_LOCK(mp);
+               if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
+                       XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+                       XFS_SB_UNLOCK(mp, s);
+                       xfs_mod_sb(args->trans,
+                                  XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+               } else
+                       XFS_SB_UNLOCK(mp, s);
+       }
 }
 
 /*
- * Remove a name from the shortform attribute list structure.
+ * Remove an attribute from the shortform attribute list structure.
  */
 int
 xfs_attr_shortform_remove(xfs_da_args_t *args)
@@ -133,17 +189,17 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
        xfs_attr_shortform_t *sf;
        xfs_attr_sf_entry_t *sfe;
        int base, size=0, end, totsize, i;
+       unsigned long s;
+       xfs_mount_t *mp;
        xfs_inode_t *dp;
 
-       /*
-        * Remove the attribute.
-        */
        dp = args->dp;
+       mp = dp->i_mount;
        base = sizeof(xfs_attr_sf_hdr_t);
        sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
        sfe = &sf->list[0];
-       for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
-                               sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
+       end = INT_GET(sf->hdr.count, ARCH_CONVERT);
+       for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
                                        base += size, i++) {
                size = XFS_ATTR_SF_ENTSIZE(sfe);
                if (sfe->namelen != args->namelen)
@@ -158,19 +214,60 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
                        continue;
                break;
        }
-       if (i == INT_GET(sf->hdr.count, ARCH_CONVERT))
+       if (i == end)
                return(XFS_ERROR(ENOATTR));
 
+       /*
+        * Fix up the attribute fork data, covering the hole
+        */
        end = base + size;
        totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
-       if (end != totsize) {
-               memmove(&((char *)sf)[base], &((char *)sf)[end],
-                                                       totsize - end);
-       }
+       if (end != totsize)
+               memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
        INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
        INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
-       xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
-       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+       /*
+        * Fix up the start offset of the attribute fork
+        */
+       totsize -= size;
+       if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) {
+               /*
+                * Last attribute now removed, revert to original
+                * inode format making all literal area available
+                * to the data fork once more.
+                */
+               xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = 0;
+               dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+               ASSERT(dp->i_d.di_anextents == 0);
+               ASSERT(dp->i_afp == NULL);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+       } else {
+               xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
+               ASSERT(dp->i_d.di_forkoff);
+               ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname);
+               dp->i_afp->if_ext_max =
+                       XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp,
+                                       XFS_ILOG_CORE | XFS_ILOG_ADATA);
+       }
+
+       if (unlikely(!XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
+               s = XFS_SB_LOCK(mp);
+               if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
+                       XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+                       XFS_SB_UNLOCK(mp, s);
+                       xfs_mod_sb(args->trans,
+                                  XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+               } else
+                       XFS_SB_UNLOCK(mp, s);
+       }
 
        return(0);
 }
@@ -299,6 +396,9 @@ out:
 /*
  * Check a leaf attribute block to see if all the entries would fit into
  * a shortform attribute list.
+ * Returns zero if not, minus one if no entries at all exist now, or the
+ * value that should be used for di_forkoff in the inode after switching
+ * it to shortform attributes.
  */
 int
 xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
@@ -328,14 +428,16 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
                                + name_loc->namelen
                                + INT_GET(name_loc->valuelen, ARCH_CONVERT);
        }
-       return( bytes < XFS_IFORK_ASIZE(dp) );
+       if (bytes == sizeof(struct xfs_attr_sf_hdr))
+               return(-1);
+       return(xfs_attr_shortform_bytesfit(dp, bytes));
 }
 
 /*
  * Convert a leaf attribute list to shortform attribute list
  */
 int
-xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
 {
        xfs_attr_leafblock_t *leaf;
        xfs_attr_leaf_entry_t *entry;
@@ -362,9 +464,25 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
        error = xfs_da_shrink_inode(args, 0, bp);
        if (error)
                goto out;
-       error = xfs_attr_shortform_create(args);
-       if (error)
+
+       if (forkoff == -1) {
+               /*
+                * Last attribute was removed, revert to original
+                * inode format making all literal area available
+                * to the data fork once more.
+                */
+               xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+               dp->i_d.di_forkoff = 0;
+               dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+               ASSERT(dp->i_d.di_anextents == 0);
+               ASSERT(dp->i_afp == NULL);
+               dp->i_df.if_ext_max =
+                       XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+               xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
                goto out;
+       }
+
+       xfs_attr_shortform_create(args);
 
        /*
         * Copy the attributes
@@ -392,7 +510,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
                nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
                nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
                              ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
-               xfs_attr_shortform_add(&nargs);
+               xfs_attr_shortform_add(&nargs, forkoff);
        }
        error = 0;
 
@@ -576,7 +694,7 @@ xfs_attr_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args)
        ASSERT((args->index >= 0)
                && (args->index <= INT_GET(leaf->hdr.count, ARCH_CONVERT)));
        hdr = &leaf->hdr;
-       entsize = xfs_attr_leaf_newentsize(args,
+       entsize = xfs_attr_leaf_newentsize(args->namelen, args->valuelen,
                           args->trans->t_mountp->m_sb.sb_blocksize, NULL);
 
        /*
@@ -673,13 +791,14 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
        mp = args->trans->t_mountp;
        ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
        ASSERT((INT_GET(map->base, ARCH_CONVERT) & 0x3) == 0);
-       ASSERT(INT_GET(map->size, ARCH_CONVERT)
-                               >= xfs_attr_leaf_newentsize(args,
-                                            mp->m_sb.sb_blocksize, NULL));
+       ASSERT(INT_GET(map->size, ARCH_CONVERT) >=
+               xfs_attr_leaf_newentsize(args->namelen, args->valuelen,
+                                        mp->m_sb.sb_blocksize, NULL));
        ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
        ASSERT((INT_GET(map->size, ARCH_CONVERT) & 0x3) == 0);
        INT_MOD(map->size, ARCH_CONVERT,
-               -xfs_attr_leaf_newentsize(args, mp->m_sb.sb_blocksize, &tmp));
+               -xfs_attr_leaf_newentsize(args->namelen, args->valuelen,
+                                         mp->m_sb.sb_blocksize, &tmp));
        INT_SET(entry->nameidx, ARCH_CONVERT,
                                        INT_GET(map->base, ARCH_CONVERT)
                                      + INT_GET(map->size, ARCH_CONVERT));
@@ -1035,8 +1154,10 @@ xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
        half  = (max+1) * sizeof(*entry);
        half += INT_GET(hdr1->usedbytes, ARCH_CONVERT)
                                + INT_GET(hdr2->usedbytes, ARCH_CONVERT)
-                               + xfs_attr_leaf_newentsize(state->args,
-                                                    state->blocksize, NULL);
+                               + xfs_attr_leaf_newentsize(
+                                               state->args->namelen,
+                                               state->args->valuelen,
+                                               state->blocksize, NULL);
        half /= 2;
        lastdelta = state->blocksize;
        entry = &leaf1->entries[0];
@@ -1048,9 +1169,10 @@ xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
                 */
                if (count == blk1->index) {
                        tmp = totallen + sizeof(*entry) +
-                               xfs_attr_leaf_newentsize(state->args,
-                                                        state->blocksize,
-                                                        NULL);
+                               xfs_attr_leaf_newentsize(
+                                               state->args->namelen,
+                                               state->args->valuelen,
+                                               state->blocksize, NULL);
                        if (XFS_ATTR_ABS(half - tmp) > lastdelta)
                                break;
                        lastdelta = XFS_ATTR_ABS(half - tmp);
@@ -1086,9 +1208,10 @@ xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
        totallen -= count * sizeof(*entry);
        if (foundit) {
                totallen -= sizeof(*entry) +
-                               xfs_attr_leaf_newentsize(state->args,
-                                                        state->blocksize,
-                                                        NULL);
+                               xfs_attr_leaf_newentsize(
+                                               state->args->namelen,
+                                               state->args->valuelen,
+                                               state->blocksize, NULL);
        }
 
        *countarg = count;
@@ -1876,17 +1999,17 @@ xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index)
  * a "local" or a "remote" attribute.
  */
 int
-xfs_attr_leaf_newentsize(xfs_da_args_t *args, int blocksize, int *local)
+xfs_attr_leaf_newentsize(int namelen, int valuelen, int blocksize, int *local)
 {
        int size;
 
-       size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(args->namelen, args->valuelen);
+       size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(namelen, valuelen);
        if (size < XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX(blocksize)) {
                if (local) {
                        *local = 1;
                }
        } else {
-               size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(args->namelen);
+               size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(namelen);
                if (local) {
                        *local = 0;
                }
@@ -2224,12 +2347,8 @@ xfs_attr_rolltrans(xfs_trans_t **transp, xfs_inode_t *dp)
        /*
         * Copy the critical parameters from one trans to the next.
         */
-#ifdef __KERNEL__
        logres = trans->t_log_res;
        count = trans->t_log_count;
-#else
-       logres = count = 0;
-#endif
        *transp = xfs_trans_dup(trans);
 
        /*