aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-08-12 06:49:38 -0400
committerBen Myers <bpm@sgi.com>2013-08-12 17:41:29 -0400
commitabec5f2bf991a03b055be36d918cd6f8f58cc83e (patch)
tree7f440cd0fb510322700b48781800b16b02b19e25 /fs
parent2b9ab5ab9c4a1f16f11ad883bfd21d5eb55e5bcc (diff)
xfs: split out attribute listing code into separate file
The attribute listing code is not used by userspace, so like the directory readdir code, split it out into a separate file to minimise the differences between the filesystem shared with libxfs in userspace. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Mark Tinguely <tinguely@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/Makefile1
-rw-r--r--fs/xfs/xfs_attr.c317
-rw-r--r--fs/xfs/xfs_attr.h1
-rw-r--r--fs/xfs/xfs_attr_leaf.c300
-rw-r--r--fs/xfs/xfs_attr_list.c655
5 files changed, 658 insertions, 616 deletions
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index b6158afa94e7..94df3ec1945f 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -27,6 +27,7 @@ xfs-y += xfs_trace.o
27 27
28# highlevel code 28# highlevel code
29xfs-y += xfs_aops.o \ 29xfs-y += xfs_aops.o \
30 xfs_attr_list.o \
30 xfs_bit.o \ 31 xfs_bit.o \
31 xfs_buf.o \ 32 xfs_buf.o \
32 xfs_dfrag.o \ 33 xfs_dfrag.o \
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index 0d92e21ca33c..e9fd5acb4305 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -63,7 +63,6 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
63STATIC int xfs_attr_leaf_get(xfs_da_args_t *args); 63STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
64STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args); 64STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
65STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); 65STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
66STATIC int xfs_attr_leaf_list(xfs_attr_list_context_t *context);
67 66
68/* 67/*
69 * Internal routines when attribute list is more than one block. 68 * Internal routines when attribute list is more than one block.
@@ -71,7 +70,6 @@ STATIC int xfs_attr_leaf_list(xfs_attr_list_context_t *context);
71STATIC int xfs_attr_node_get(xfs_da_args_t *args); 70STATIC int xfs_attr_node_get(xfs_da_args_t *args);
72STATIC int xfs_attr_node_addname(xfs_da_args_t *args); 71STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
73STATIC int xfs_attr_node_removename(xfs_da_args_t *args); 72STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
74STATIC int xfs_attr_node_list(xfs_attr_list_context_t *context);
75STATIC int xfs_attr_fillstate(xfs_da_state_t *state); 73STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
76STATIC int xfs_attr_refillstate(xfs_da_state_t *state); 74STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
77 75
@@ -91,7 +89,7 @@ xfs_attr_name_to_xname(
91 return 0; 89 return 0;
92} 90}
93 91
94STATIC int 92int
95xfs_inode_hasattr( 93xfs_inode_hasattr(
96 struct xfs_inode *ip) 94 struct xfs_inode *ip)
97{ 95{
@@ -612,157 +610,6 @@ xfs_attr_remove(
612 return xfs_attr_remove_int(dp, &xname, flags); 610 return xfs_attr_remove_int(dp, &xname, flags);
613} 611}
614 612
615int
616xfs_attr_list_int(xfs_attr_list_context_t *context)
617{
618 int error;
619 xfs_inode_t *dp = context->dp;
620
621 XFS_STATS_INC(xs_attr_list);
622
623 if (XFS_FORCED_SHUTDOWN(dp->i_mount))
624 return EIO;
625
626 xfs_ilock(dp, XFS_ILOCK_SHARED);
627
628 /*
629 * Decide on what work routines to call based on the inode size.
630 */
631 if (!xfs_inode_hasattr(dp)) {
632 error = 0;
633 } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
634 error = xfs_attr_shortform_list(context);
635 } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
636 error = xfs_attr_leaf_list(context);
637 } else {
638 error = xfs_attr_node_list(context);
639 }
640
641 xfs_iunlock(dp, XFS_ILOCK_SHARED);
642
643 return error;
644}
645
646#define ATTR_ENTBASESIZE /* minimum bytes used by an attr */ \
647 (((struct attrlist_ent *) 0)->a_name - (char *) 0)
648#define ATTR_ENTSIZE(namelen) /* actual bytes used by an attr */ \
649 ((ATTR_ENTBASESIZE + (namelen) + 1 + sizeof(u_int32_t)-1) \
650 & ~(sizeof(u_int32_t)-1))
651
652/*
653 * Format an attribute and copy it out to the user's buffer.
654 * Take care to check values and protect against them changing later,
655 * we may be reading them directly out of a user buffer.
656 */
657/*ARGSUSED*/
658STATIC int
659xfs_attr_put_listent(
660 xfs_attr_list_context_t *context,
661 int flags,
662 unsigned char *name,
663 int namelen,
664 int valuelen,
665 unsigned char *value)
666{
667 struct attrlist *alist = (struct attrlist *)context->alist;
668 attrlist_ent_t *aep;
669 int arraytop;
670
671 ASSERT(!(context->flags & ATTR_KERNOVAL));
672 ASSERT(context->count >= 0);
673 ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
674 ASSERT(context->firstu >= sizeof(*alist));
675 ASSERT(context->firstu <= context->bufsize);
676
677 /*
678 * Only list entries in the right namespace.
679 */
680 if (((context->flags & ATTR_SECURE) == 0) !=
681 ((flags & XFS_ATTR_SECURE) == 0))
682 return 0;
683 if (((context->flags & ATTR_ROOT) == 0) !=
684 ((flags & XFS_ATTR_ROOT) == 0))
685 return 0;
686
687 arraytop = sizeof(*alist) +
688 context->count * sizeof(alist->al_offset[0]);
689 context->firstu -= ATTR_ENTSIZE(namelen);
690 if (context->firstu < arraytop) {
691 trace_xfs_attr_list_full(context);
692 alist->al_more = 1;
693 context->seen_enough = 1;
694 return 1;
695 }
696
697 aep = (attrlist_ent_t *)&context->alist[context->firstu];
698 aep->a_valuelen = valuelen;
699 memcpy(aep->a_name, name, namelen);
700 aep->a_name[namelen] = 0;
701 alist->al_offset[context->count++] = context->firstu;
702 alist->al_count = context->count;
703 trace_xfs_attr_list_add(context);
704 return 0;
705}
706
707/*
708 * Generate a list of extended attribute names and optionally
709 * also value lengths. Positive return value follows the XFS
710 * convention of being an error, zero or negative return code
711 * is the length of the buffer returned (negated), indicating
712 * success.
713 */
714int
715xfs_attr_list(
716 xfs_inode_t *dp,
717 char *buffer,
718 int bufsize,
719 int flags,
720 attrlist_cursor_kern_t *cursor)
721{
722 xfs_attr_list_context_t context;
723 struct attrlist *alist;
724 int error;
725
726 /*
727 * Validate the cursor.
728 */
729 if (cursor->pad1 || cursor->pad2)
730 return(XFS_ERROR(EINVAL));
731 if ((cursor->initted == 0) &&
732 (cursor->hashval || cursor->blkno || cursor->offset))
733 return XFS_ERROR(EINVAL);
734
735 /*
736 * Check for a properly aligned buffer.
737 */
738 if (((long)buffer) & (sizeof(int)-1))
739 return XFS_ERROR(EFAULT);
740 if (flags & ATTR_KERNOVAL)
741 bufsize = 0;
742
743 /*
744 * Initialize the output buffer.
745 */
746 memset(&context, 0, sizeof(context));
747 context.dp = dp;
748 context.cursor = cursor;
749 context.resynch = 1;
750 context.flags = flags;
751 context.alist = buffer;
752 context.bufsize = (bufsize & ~(sizeof(int)-1)); /* align */
753 context.firstu = context.bufsize;
754 context.put_listent = xfs_attr_put_listent;
755
756 alist = (struct attrlist *)context.alist;
757 alist->al_count = 0;
758 alist->al_more = 0;
759 alist->al_offset[0] = context.bufsize;
760
761 error = xfs_attr_list_int(&context);
762 ASSERT(error >= 0);
763 return error;
764}
765
766int /* error */ 613int /* error */
767xfs_attr_inactive(xfs_inode_t *dp) 614xfs_attr_inactive(xfs_inode_t *dp)
768{ 615{
@@ -1167,28 +1014,6 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
1167 return error; 1014 return error;
1168} 1015}
1169 1016
1170/*
1171 * Copy out attribute entries for attr_list(), for leaf attribute lists.
1172 */
1173STATIC int
1174xfs_attr_leaf_list(xfs_attr_list_context_t *context)
1175{
1176 int error;
1177 struct xfs_buf *bp;
1178
1179 trace_xfs_attr_leaf_list(context);
1180
1181 context->cursor->blkno = 0;
1182 error = xfs_attr3_leaf_read(NULL, context->dp, 0, -1, &bp);
1183 if (error)
1184 return XFS_ERROR(error);
1185
1186 error = xfs_attr3_leaf_list_int(bp, context);
1187 xfs_trans_brelse(NULL, bp);
1188 return XFS_ERROR(error);
1189}
1190
1191
1192/*======================================================================== 1017/*========================================================================
1193 * External routines when attribute list size > XFS_LBSIZE(mp). 1018 * External routines when attribute list size > XFS_LBSIZE(mp).
1194 *========================================================================*/ 1019 *========================================================================*/
@@ -1781,143 +1606,3 @@ xfs_attr_node_get(xfs_da_args_t *args)
1781 xfs_da_state_free(state); 1606 xfs_da_state_free(state);
1782 return(retval); 1607 return(retval);
1783} 1608}
1784
1785STATIC int /* error */
1786xfs_attr_node_list(xfs_attr_list_context_t *context)
1787{
1788 attrlist_cursor_kern_t *cursor;
1789 xfs_attr_leafblock_t *leaf;
1790 xfs_da_intnode_t *node;
1791 struct xfs_attr3_icleaf_hdr leafhdr;
1792 struct xfs_da3_icnode_hdr nodehdr;
1793 struct xfs_da_node_entry *btree;
1794 int error, i;
1795 struct xfs_buf *bp;
1796
1797 trace_xfs_attr_node_list(context);
1798
1799 cursor = context->cursor;
1800 cursor->initted = 1;
1801
1802 /*
1803 * Do all sorts of validation on the passed-in cursor structure.
1804 * If anything is amiss, ignore the cursor and look up the hashval
1805 * starting from the btree root.
1806 */
1807 bp = NULL;
1808 if (cursor->blkno > 0) {
1809 error = xfs_da3_node_read(NULL, context->dp, cursor->blkno, -1,
1810 &bp, XFS_ATTR_FORK);
1811 if ((error != 0) && (error != EFSCORRUPTED))
1812 return(error);
1813 if (bp) {
1814 struct xfs_attr_leaf_entry *entries;
1815
1816 node = bp->b_addr;
1817 switch (be16_to_cpu(node->hdr.info.magic)) {
1818 case XFS_DA_NODE_MAGIC:
1819 case XFS_DA3_NODE_MAGIC:
1820 trace_xfs_attr_list_wrong_blk(context);
1821 xfs_trans_brelse(NULL, bp);
1822 bp = NULL;
1823 break;
1824 case XFS_ATTR_LEAF_MAGIC:
1825 case XFS_ATTR3_LEAF_MAGIC:
1826 leaf = bp->b_addr;
1827 xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
1828 entries = xfs_attr3_leaf_entryp(leaf);
1829 if (cursor->hashval > be32_to_cpu(
1830 entries[leafhdr.count - 1].hashval)) {
1831 trace_xfs_attr_list_wrong_blk(context);
1832 xfs_trans_brelse(NULL, bp);
1833 bp = NULL;
1834 } else if (cursor->hashval <= be32_to_cpu(
1835 entries[0].hashval)) {
1836 trace_xfs_attr_list_wrong_blk(context);
1837 xfs_trans_brelse(NULL, bp);
1838 bp = NULL;
1839 }
1840 break;
1841 default:
1842 trace_xfs_attr_list_wrong_blk(context);
1843 xfs_trans_brelse(NULL, bp);
1844 bp = NULL;
1845 }
1846 }
1847 }
1848
1849 /*
1850 * We did not find what we expected given the cursor's contents,
1851 * so we start from the top and work down based on the hash value.
1852 * Note that start of node block is same as start of leaf block.
1853 */
1854 if (bp == NULL) {
1855 cursor->blkno = 0;
1856 for (;;) {
1857 __uint16_t magic;
1858
1859 error = xfs_da3_node_read(NULL, context->dp,
1860 cursor->blkno, -1, &bp,
1861 XFS_ATTR_FORK);
1862 if (error)
1863 return(error);
1864 node = bp->b_addr;
1865 magic = be16_to_cpu(node->hdr.info.magic);
1866 if (magic == XFS_ATTR_LEAF_MAGIC ||
1867 magic == XFS_ATTR3_LEAF_MAGIC)
1868 break;
1869 if (magic != XFS_DA_NODE_MAGIC &&
1870 magic != XFS_DA3_NODE_MAGIC) {
1871 XFS_CORRUPTION_ERROR("xfs_attr_node_list(3)",
1872 XFS_ERRLEVEL_LOW,
1873 context->dp->i_mount,
1874 node);
1875 xfs_trans_brelse(NULL, bp);
1876 return XFS_ERROR(EFSCORRUPTED);
1877 }
1878
1879 xfs_da3_node_hdr_from_disk(&nodehdr, node);
1880 btree = xfs_da3_node_tree_p(node);
1881 for (i = 0; i < nodehdr.count; btree++, i++) {
1882 if (cursor->hashval
1883 <= be32_to_cpu(btree->hashval)) {
1884 cursor->blkno = be32_to_cpu(btree->before);
1885 trace_xfs_attr_list_node_descend(context,
1886 btree);
1887 break;
1888 }
1889 }
1890 if (i == nodehdr.count) {
1891 xfs_trans_brelse(NULL, bp);
1892 return 0;
1893 }
1894 xfs_trans_brelse(NULL, bp);
1895 }
1896 }
1897 ASSERT(bp != NULL);
1898
1899 /*
1900 * Roll upward through the blocks, processing each leaf block in
1901 * order. As long as there is space in the result buffer, keep
1902 * adding the information.
1903 */
1904 for (;;) {
1905 leaf = bp->b_addr;
1906 error = xfs_attr3_leaf_list_int(bp, context);
1907 if (error) {
1908 xfs_trans_brelse(NULL, bp);
1909 return error;
1910 }
1911 xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
1912 if (context->seen_enough || leafhdr.forw == 0)
1913 break;
1914 cursor->blkno = leafhdr.forw;
1915 xfs_trans_brelse(NULL, bp);
1916 error = xfs_attr3_leaf_read(NULL, context->dp, cursor->blkno, -1,
1917 &bp);
1918 if (error)
1919 return error;
1920 }
1921 xfs_trans_brelse(NULL, bp);
1922 return 0;
1923}
diff --git a/fs/xfs/xfs_attr.h b/fs/xfs/xfs_attr.h
index de8dd58da46c..cb604b53e2f6 100644
--- a/fs/xfs/xfs_attr.h
+++ b/fs/xfs/xfs_attr.h
@@ -141,5 +141,6 @@ typedef struct xfs_attr_list_context {
141 */ 141 */
142int xfs_attr_inactive(struct xfs_inode *dp); 142int xfs_attr_inactive(struct xfs_inode *dp);
143int xfs_attr_list_int(struct xfs_attr_list_context *); 143int xfs_attr_list_int(struct xfs_attr_list_context *);
144int xfs_inode_hasattr(struct xfs_inode *ip);
144 145
145#endif /* __XFS_ATTR_H__ */ 146#endif /* __XFS_ATTR_H__ */
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index e6497d0c2a72..90c033e41eb9 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -752,182 +752,6 @@ out:
752 return(error); 752 return(error);
753} 753}
754 754
755STATIC int
756xfs_attr_shortform_compare(const void *a, const void *b)
757{
758 xfs_attr_sf_sort_t *sa, *sb;
759
760 sa = (xfs_attr_sf_sort_t *)a;
761 sb = (xfs_attr_sf_sort_t *)b;
762 if (sa->hash < sb->hash) {
763 return(-1);
764 } else if (sa->hash > sb->hash) {
765 return(1);
766 } else {
767 return(sa->entno - sb->entno);
768 }
769}
770
771
772#define XFS_ISRESET_CURSOR(cursor) \
773 (!((cursor)->initted) && !((cursor)->hashval) && \
774 !((cursor)->blkno) && !((cursor)->offset))
775/*
776 * Copy out entries of shortform attribute lists for attr_list().
777 * Shortform attribute lists are not stored in hashval sorted order.
778 * If the output buffer is not large enough to hold them all, then we
779 * we have to calculate each entries' hashvalue and sort them before
780 * we can begin returning them to the user.
781 */
782/*ARGSUSED*/
783int
784xfs_attr_shortform_list(xfs_attr_list_context_t *context)
785{
786 attrlist_cursor_kern_t *cursor;
787 xfs_attr_sf_sort_t *sbuf, *sbp;
788 xfs_attr_shortform_t *sf;
789 xfs_attr_sf_entry_t *sfe;
790 xfs_inode_t *dp;
791 int sbsize, nsbuf, count, i;
792 int error;
793
794 ASSERT(context != NULL);
795 dp = context->dp;
796 ASSERT(dp != NULL);
797 ASSERT(dp->i_afp != NULL);
798 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
799 ASSERT(sf != NULL);
800 if (!sf->hdr.count)
801 return(0);
802 cursor = context->cursor;
803 ASSERT(cursor != NULL);
804
805 trace_xfs_attr_list_sf(context);
806
807 /*
808 * If the buffer is large enough and the cursor is at the start,
809 * do not bother with sorting since we will return everything in
810 * one buffer and another call using the cursor won't need to be
811 * made.
812 * Note the generous fudge factor of 16 overhead bytes per entry.
813 * If bufsize is zero then put_listent must be a search function
814 * and can just scan through what we have.
815 */
816 if (context->bufsize == 0 ||
817 (XFS_ISRESET_CURSOR(cursor) &&
818 (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) {
819 for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
820 error = context->put_listent(context,
821 sfe->flags,
822 sfe->nameval,
823 (int)sfe->namelen,
824 (int)sfe->valuelen,
825 &sfe->nameval[sfe->namelen]);
826
827 /*
828 * Either search callback finished early or
829 * didn't fit it all in the buffer after all.
830 */
831 if (context->seen_enough)
832 break;
833
834 if (error)
835 return error;
836 sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
837 }
838 trace_xfs_attr_list_sf_all(context);
839 return(0);
840 }
841
842 /* do no more for a search callback */
843 if (context->bufsize == 0)
844 return 0;
845
846 /*
847 * It didn't all fit, so we have to sort everything on hashval.
848 */
849 sbsize = sf->hdr.count * sizeof(*sbuf);
850 sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP | KM_NOFS);
851
852 /*
853 * Scan the attribute list for the rest of the entries, storing
854 * the relevant info from only those that match into a buffer.
855 */
856 nsbuf = 0;
857 for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
858 if (unlikely(
859 ((char *)sfe < (char *)sf) ||
860 ((char *)sfe >= ((char *)sf + dp->i_afp->if_bytes)))) {
861 XFS_CORRUPTION_ERROR("xfs_attr_shortform_list",
862 XFS_ERRLEVEL_LOW,
863 context->dp->i_mount, sfe);
864 kmem_free(sbuf);
865 return XFS_ERROR(EFSCORRUPTED);
866 }
867
868 sbp->entno = i;
869 sbp->hash = xfs_da_hashname(sfe->nameval, sfe->namelen);
870 sbp->name = sfe->nameval;
871 sbp->namelen = sfe->namelen;
872 /* These are bytes, and both on-disk, don't endian-flip */
873 sbp->valuelen = sfe->valuelen;
874 sbp->flags = sfe->flags;
875 sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
876 sbp++;
877 nsbuf++;
878 }
879
880 /*
881 * Sort the entries on hash then entno.
882 */
883 xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_attr_shortform_compare);
884
885 /*
886 * Re-find our place IN THE SORTED LIST.
887 */
888 count = 0;
889 cursor->initted = 1;
890 cursor->blkno = 0;
891 for (sbp = sbuf, i = 0; i < nsbuf; i++, sbp++) {
892 if (sbp->hash == cursor->hashval) {
893 if (cursor->offset == count) {
894 break;
895 }
896 count++;
897 } else if (sbp->hash > cursor->hashval) {
898 break;
899 }
900 }
901 if (i == nsbuf) {
902 kmem_free(sbuf);
903 return(0);
904 }
905
906 /*
907 * Loop putting entries into the user buffer.
908 */
909 for ( ; i < nsbuf; i++, sbp++) {
910 if (cursor->hashval != sbp->hash) {
911 cursor->hashval = sbp->hash;
912 cursor->offset = 0;
913 }
914 error = context->put_listent(context,
915 sbp->flags,
916 sbp->name,
917 sbp->namelen,
918 sbp->valuelen,
919 &sbp->name[sbp->namelen]);
920 if (error)
921 return error;
922 if (context->seen_enough)
923 break;
924 cursor->offset++;
925 }
926
927 kmem_free(sbuf);
928 return(0);
929}
930
931/* 755/*
932 * Check a leaf attribute block to see if all the entries would fit into 756 * Check a leaf attribute block to see if all the entries would fit into
933 * a shortform attribute list. 757 * a shortform attribute list.
@@ -2644,130 +2468,6 @@ xfs_attr_leaf_newentsize(int namelen, int valuelen, int blocksize, int *local)
2644 return size; 2468 return size;
2645} 2469}
2646 2470
2647/*
2648 * Copy out attribute list entries for attr_list(), for leaf attribute lists.
2649 */
2650int
2651xfs_attr3_leaf_list_int(
2652 struct xfs_buf *bp,
2653 struct xfs_attr_list_context *context)
2654{
2655 struct attrlist_cursor_kern *cursor;
2656 struct xfs_attr_leafblock *leaf;
2657 struct xfs_attr3_icleaf_hdr ichdr;
2658 struct xfs_attr_leaf_entry *entries;
2659 struct xfs_attr_leaf_entry *entry;
2660 int retval;
2661 int i;
2662
2663 trace_xfs_attr_list_leaf(context);
2664
2665 leaf = bp->b_addr;
2666 xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf);
2667 entries = xfs_attr3_leaf_entryp(leaf);
2668
2669 cursor = context->cursor;
2670 cursor->initted = 1;
2671
2672 /*
2673 * Re-find our place in the leaf block if this is a new syscall.
2674 */
2675 if (context->resynch) {
2676 entry = &entries[0];
2677 for (i = 0; i < ichdr.count; entry++, i++) {
2678 if (be32_to_cpu(entry->hashval) == cursor->hashval) {
2679 if (cursor->offset == context->dupcnt) {
2680 context->dupcnt = 0;
2681 break;
2682 }
2683 context->dupcnt++;
2684 } else if (be32_to_cpu(entry->hashval) >
2685 cursor->hashval) {
2686 context->dupcnt = 0;
2687 break;
2688 }
2689 }
2690 if (i == ichdr.count) {
2691 trace_xfs_attr_list_notfound(context);
2692 return 0;
2693 }
2694 } else {
2695 entry = &entries[0];
2696 i = 0;
2697 }
2698 context->resynch = 0;
2699
2700 /*
2701 * We have found our place, start copying out the new attributes.
2702 */
2703 retval = 0;
2704 for (; i < ichdr.count; entry++, i++) {
2705 if (be32_to_cpu(entry->hashval) != cursor->hashval) {
2706 cursor->hashval = be32_to_cpu(entry->hashval);
2707 cursor->offset = 0;
2708 }
2709
2710 if (entry->flags & XFS_ATTR_INCOMPLETE)
2711 continue; /* skip incomplete entries */
2712
2713 if (entry->flags & XFS_ATTR_LOCAL) {
2714 xfs_attr_leaf_name_local_t *name_loc =
2715 xfs_attr3_leaf_name_local(leaf, i);
2716
2717 retval = context->put_listent(context,
2718 entry->flags,
2719 name_loc->nameval,
2720 (int)name_loc->namelen,
2721 be16_to_cpu(name_loc->valuelen),
2722 &name_loc->nameval[name_loc->namelen]);
2723 if (retval)
2724 return retval;
2725 } else {
2726 xfs_attr_leaf_name_remote_t *name_rmt =
2727 xfs_attr3_leaf_name_remote(leaf, i);
2728
2729 int valuelen = be32_to_cpu(name_rmt->valuelen);
2730
2731 if (context->put_value) {
2732 xfs_da_args_t args;
2733
2734 memset((char *)&args, 0, sizeof(args));
2735 args.dp = context->dp;
2736 args.whichfork = XFS_ATTR_FORK;
2737 args.valuelen = valuelen;
2738 args.value = kmem_alloc(valuelen, KM_SLEEP | KM_NOFS);
2739 args.rmtblkno = be32_to_cpu(name_rmt->valueblk);
2740 args.rmtblkcnt = xfs_attr3_rmt_blocks(
2741 args.dp->i_mount, valuelen);
2742 retval = xfs_attr_rmtval_get(&args);
2743 if (retval)
2744 return retval;
2745 retval = context->put_listent(context,
2746 entry->flags,
2747 name_rmt->name,
2748 (int)name_rmt->namelen,
2749 valuelen,
2750 args.value);
2751 kmem_free(args.value);
2752 } else {
2753 retval = context->put_listent(context,
2754 entry->flags,
2755 name_rmt->name,
2756 (int)name_rmt->namelen,
2757 valuelen,
2758 NULL);
2759 }
2760 if (retval)
2761 return retval;
2762 }
2763 if (context->seen_enough)
2764 break;
2765 cursor->offset++;
2766 }
2767 trace_xfs_attr_list_leaf_end(context);
2768 return retval;
2769}
2770
2771 2471
2772/*======================================================================== 2472/*========================================================================
2773 * Manage the INCOMPLETE flag in a leaf entry 2473 * Manage the INCOMPLETE flag in a leaf entry
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
new file mode 100644
index 000000000000..cbc80d485177
--- /dev/null
+++ b/fs/xfs/xfs_attr_list.c
@@ -0,0 +1,655 @@
1/*
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * Copyright (c) 2013 Red Hat, Inc.
4 * All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it would be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include "xfs.h"
20#include "xfs_fs.h"
21#include "xfs_types.h"
22#include "xfs_bit.h"
23#include "xfs_log.h"
24#include "xfs_trans.h"
25#include "xfs_sb.h"
26#include "xfs_ag.h"
27#include "xfs_mount.h"
28#include "xfs_da_btree.h"
29#include "xfs_bmap_btree.h"
30#include "xfs_alloc_btree.h"
31#include "xfs_ialloc_btree.h"
32#include "xfs_alloc.h"
33#include "xfs_btree.h"
34#include "xfs_attr_sf.h"
35#include "xfs_attr_remote.h"
36#include "xfs_dinode.h"
37#include "xfs_inode.h"
38#include "xfs_inode_item.h"
39#include "xfs_bmap.h"
40#include "xfs_attr.h"
41#include "xfs_attr_leaf.h"
42#include "xfs_error.h"
43#include "xfs_trace.h"
44#include "xfs_buf_item.h"
45#include "xfs_cksum.h"
46
47STATIC int
48xfs_attr_shortform_compare(const void *a, const void *b)
49{
50 xfs_attr_sf_sort_t *sa, *sb;
51
52 sa = (xfs_attr_sf_sort_t *)a;
53 sb = (xfs_attr_sf_sort_t *)b;
54 if (sa->hash < sb->hash) {
55 return(-1);
56 } else if (sa->hash > sb->hash) {
57 return(1);
58 } else {
59 return(sa->entno - sb->entno);
60 }
61}
62
63#define XFS_ISRESET_CURSOR(cursor) \
64 (!((cursor)->initted) && !((cursor)->hashval) && \
65 !((cursor)->blkno) && !((cursor)->offset))
66/*
67 * Copy out entries of shortform attribute lists for attr_list().
68 * Shortform attribute lists are not stored in hashval sorted order.
69 * If the output buffer is not large enough to hold them all, then we
70 * we have to calculate each entries' hashvalue and sort them before
71 * we can begin returning them to the user.
72 */
73int
74xfs_attr_shortform_list(xfs_attr_list_context_t *context)
75{
76 attrlist_cursor_kern_t *cursor;
77 xfs_attr_sf_sort_t *sbuf, *sbp;
78 xfs_attr_shortform_t *sf;
79 xfs_attr_sf_entry_t *sfe;
80 xfs_inode_t *dp;
81 int sbsize, nsbuf, count, i;
82 int error;
83
84 ASSERT(context != NULL);
85 dp = context->dp;
86 ASSERT(dp != NULL);
87 ASSERT(dp->i_afp != NULL);
88 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
89 ASSERT(sf != NULL);
90 if (!sf->hdr.count)
91 return(0);
92 cursor = context->cursor;
93 ASSERT(cursor != NULL);
94
95 trace_xfs_attr_list_sf(context);
96
97 /*
98 * If the buffer is large enough and the cursor is at the start,
99 * do not bother with sorting since we will return everything in
100 * one buffer and another call using the cursor won't need to be
101 * made.
102 * Note the generous fudge factor of 16 overhead bytes per entry.
103 * If bufsize is zero then put_listent must be a search function
104 * and can just scan through what we have.
105 */
106 if (context->bufsize == 0 ||
107 (XFS_ISRESET_CURSOR(cursor) &&
108 (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) {
109 for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
110 error = context->put_listent(context,
111 sfe->flags,
112 sfe->nameval,
113 (int)sfe->namelen,
114 (int)sfe->valuelen,
115 &sfe->nameval[sfe->namelen]);
116
117 /*
118 * Either search callback finished early or
119 * didn't fit it all in the buffer after all.
120 */
121 if (context->seen_enough)
122 break;
123
124 if (error)
125 return error;
126 sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
127 }
128 trace_xfs_attr_list_sf_all(context);
129 return(0);
130 }
131
132 /* do no more for a search callback */
133 if (context->bufsize == 0)
134 return 0;
135
136 /*
137 * It didn't all fit, so we have to sort everything on hashval.
138 */
139 sbsize = sf->hdr.count * sizeof(*sbuf);
140 sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP | KM_NOFS);
141
142 /*
143 * Scan the attribute list for the rest of the entries, storing
144 * the relevant info from only those that match into a buffer.
145 */
146 nsbuf = 0;
147 for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
148 if (unlikely(
149 ((char *)sfe < (char *)sf) ||
150 ((char *)sfe >= ((char *)sf + dp->i_afp->if_bytes)))) {
151 XFS_CORRUPTION_ERROR("xfs_attr_shortform_list",
152 XFS_ERRLEVEL_LOW,
153 context->dp->i_mount, sfe);
154 kmem_free(sbuf);
155 return XFS_ERROR(EFSCORRUPTED);
156 }
157
158 sbp->entno = i;
159 sbp->hash = xfs_da_hashname(sfe->nameval, sfe->namelen);
160 sbp->name = sfe->nameval;
161 sbp->namelen = sfe->namelen;
162 /* These are bytes, and both on-disk, don't endian-flip */
163 sbp->valuelen = sfe->valuelen;
164 sbp->flags = sfe->flags;
165 sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
166 sbp++;
167 nsbuf++;
168 }
169
170 /*
171 * Sort the entries on hash then entno.
172 */
173 xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_attr_shortform_compare);
174
175 /*
176 * Re-find our place IN THE SORTED LIST.
177 */
178 count = 0;
179 cursor->initted = 1;
180 cursor->blkno = 0;
181 for (sbp = sbuf, i = 0; i < nsbuf; i++, sbp++) {
182 if (sbp->hash == cursor->hashval) {
183 if (cursor->offset == count) {
184 break;
185 }
186 count++;
187 } else if (sbp->hash > cursor->hashval) {
188 break;
189 }
190 }
191 if (i == nsbuf) {
192 kmem_free(sbuf);
193 return(0);
194 }
195
196 /*
197 * Loop putting entries into the user buffer.
198 */
199 for ( ; i < nsbuf; i++, sbp++) {
200 if (cursor->hashval != sbp->hash) {
201 cursor->hashval = sbp->hash;
202 cursor->offset = 0;
203 }
204 error = context->put_listent(context,
205 sbp->flags,
206 sbp->name,
207 sbp->namelen,
208 sbp->valuelen,
209 &sbp->name[sbp->namelen]);
210 if (error)
211 return error;
212 if (context->seen_enough)
213 break;
214 cursor->offset++;
215 }
216
217 kmem_free(sbuf);
218 return(0);
219}
220
221STATIC int
222xfs_attr_node_list(xfs_attr_list_context_t *context)
223{
224 attrlist_cursor_kern_t *cursor;
225 xfs_attr_leafblock_t *leaf;
226 xfs_da_intnode_t *node;
227 struct xfs_attr3_icleaf_hdr leafhdr;
228 struct xfs_da3_icnode_hdr nodehdr;
229 struct xfs_da_node_entry *btree;
230 int error, i;
231 struct xfs_buf *bp;
232
233 trace_xfs_attr_node_list(context);
234
235 cursor = context->cursor;
236 cursor->initted = 1;
237
238 /*
239 * Do all sorts of validation on the passed-in cursor structure.
240 * If anything is amiss, ignore the cursor and look up the hashval
241 * starting from the btree root.
242 */
243 bp = NULL;
244 if (cursor->blkno > 0) {
245 error = xfs_da3_node_read(NULL, context->dp, cursor->blkno, -1,
246 &bp, XFS_ATTR_FORK);
247 if ((error != 0) && (error != EFSCORRUPTED))
248 return(error);
249 if (bp) {
250 struct xfs_attr_leaf_entry *entries;
251
252 node = bp->b_addr;
253 switch (be16_to_cpu(node->hdr.info.magic)) {
254 case XFS_DA_NODE_MAGIC:
255 case XFS_DA3_NODE_MAGIC:
256 trace_xfs_attr_list_wrong_blk(context);
257 xfs_trans_brelse(NULL, bp);
258 bp = NULL;
259 break;
260 case XFS_ATTR_LEAF_MAGIC:
261 case XFS_ATTR3_LEAF_MAGIC:
262 leaf = bp->b_addr;
263 xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
264 entries = xfs_attr3_leaf_entryp(leaf);
265 if (cursor->hashval > be32_to_cpu(
266 entries[leafhdr.count - 1].hashval)) {
267 trace_xfs_attr_list_wrong_blk(context);
268 xfs_trans_brelse(NULL, bp);
269 bp = NULL;
270 } else if (cursor->hashval <= be32_to_cpu(
271 entries[0].hashval)) {
272 trace_xfs_attr_list_wrong_blk(context);
273 xfs_trans_brelse(NULL, bp);
274 bp = NULL;
275 }
276 break;
277 default:
278 trace_xfs_attr_list_wrong_blk(context);
279 xfs_trans_brelse(NULL, bp);
280 bp = NULL;
281 }
282 }
283 }
284
285 /*
286 * We did not find what we expected given the cursor's contents,
287 * so we start from the top and work down based on the hash value.
288 * Note that start of node block is same as start of leaf block.
289 */
290 if (bp == NULL) {
291 cursor->blkno = 0;
292 for (;;) {
293 __uint16_t magic;
294
295 error = xfs_da3_node_read(NULL, context->dp,
296 cursor->blkno, -1, &bp,
297 XFS_ATTR_FORK);
298 if (error)
299 return(error);
300 node = bp->b_addr;
301 magic = be16_to_cpu(node->hdr.info.magic);
302 if (magic == XFS_ATTR_LEAF_MAGIC ||
303 magic == XFS_ATTR3_LEAF_MAGIC)
304 break;
305 if (magic != XFS_DA_NODE_MAGIC &&
306 magic != XFS_DA3_NODE_MAGIC) {
307 XFS_CORRUPTION_ERROR("xfs_attr_node_list(3)",
308 XFS_ERRLEVEL_LOW,
309 context->dp->i_mount,
310 node);
311 xfs_trans_brelse(NULL, bp);
312 return XFS_ERROR(EFSCORRUPTED);
313 }
314
315 xfs_da3_node_hdr_from_disk(&nodehdr, node);
316 btree = xfs_da3_node_tree_p(node);
317 for (i = 0; i < nodehdr.count; btree++, i++) {
318 if (cursor->hashval
319 <= be32_to_cpu(btree->hashval)) {
320 cursor->blkno = be32_to_cpu(btree->before);
321 trace_xfs_attr_list_node_descend(context,
322 btree);
323 break;
324 }
325 }
326 if (i == nodehdr.count) {
327 xfs_trans_brelse(NULL, bp);
328 return 0;
329 }
330 xfs_trans_brelse(NULL, bp);
331 }
332 }
333 ASSERT(bp != NULL);
334
335 /*
336 * Roll upward through the blocks, processing each leaf block in
337 * order. As long as there is space in the result buffer, keep
338 * adding the information.
339 */
340 for (;;) {
341 leaf = bp->b_addr;
342 error = xfs_attr3_leaf_list_int(bp, context);
343 if (error) {
344 xfs_trans_brelse(NULL, bp);
345 return error;
346 }
347 xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
348 if (context->seen_enough || leafhdr.forw == 0)
349 break;
350 cursor->blkno = leafhdr.forw;
351 xfs_trans_brelse(NULL, bp);
352 error = xfs_attr3_leaf_read(NULL, context->dp, cursor->blkno, -1,
353 &bp);
354 if (error)
355 return error;
356 }
357 xfs_trans_brelse(NULL, bp);
358 return 0;
359}
360
361/*
362 * Copy out attribute list entries for attr_list(), for leaf attribute lists.
363 */
364int
365xfs_attr3_leaf_list_int(
366 struct xfs_buf *bp,
367 struct xfs_attr_list_context *context)
368{
369 struct attrlist_cursor_kern *cursor;
370 struct xfs_attr_leafblock *leaf;
371 struct xfs_attr3_icleaf_hdr ichdr;
372 struct xfs_attr_leaf_entry *entries;
373 struct xfs_attr_leaf_entry *entry;
374 int retval;
375 int i;
376
377 trace_xfs_attr_list_leaf(context);
378
379 leaf = bp->b_addr;
380 xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf);
381 entries = xfs_attr3_leaf_entryp(leaf);
382
383 cursor = context->cursor;
384 cursor->initted = 1;
385
386 /*
387 * Re-find our place in the leaf block if this is a new syscall.
388 */
389 if (context->resynch) {
390 entry = &entries[0];
391 for (i = 0; i < ichdr.count; entry++, i++) {
392 if (be32_to_cpu(entry->hashval) == cursor->hashval) {
393 if (cursor->offset == context->dupcnt) {
394 context->dupcnt = 0;
395 break;
396 }
397 context->dupcnt++;
398 } else if (be32_to_cpu(entry->hashval) >
399 cursor->hashval) {
400 context->dupcnt = 0;
401 break;
402 }
403 }
404 if (i == ichdr.count) {
405 trace_xfs_attr_list_notfound(context);
406 return 0;
407 }
408 } else {
409 entry = &entries[0];
410 i = 0;
411 }
412 context->resynch = 0;
413
414 /*
415 * We have found our place, start copying out the new attributes.
416 */
417 retval = 0;
418 for (; i < ichdr.count; entry++, i++) {
419 if (be32_to_cpu(entry->hashval) != cursor->hashval) {
420 cursor->hashval = be32_to_cpu(entry->hashval);
421 cursor->offset = 0;
422 }
423
424 if (entry->flags & XFS_ATTR_INCOMPLETE)
425 continue; /* skip incomplete entries */
426
427 if (entry->flags & XFS_ATTR_LOCAL) {
428 xfs_attr_leaf_name_local_t *name_loc =
429 xfs_attr3_leaf_name_local(leaf, i);
430
431 retval = context->put_listent(context,
432 entry->flags,
433 name_loc->nameval,
434 (int)name_loc->namelen,
435 be16_to_cpu(name_loc->valuelen),
436 &name_loc->nameval[name_loc->namelen]);
437 if (retval)
438 return retval;
439 } else {
440 xfs_attr_leaf_name_remote_t *name_rmt =
441 xfs_attr3_leaf_name_remote(leaf, i);
442
443 int valuelen = be32_to_cpu(name_rmt->valuelen);
444
445 if (context->put_value) {
446 xfs_da_args_t args;
447
448 memset((char *)&args, 0, sizeof(args));
449 args.dp = context->dp;
450 args.whichfork = XFS_ATTR_FORK;
451 args.valuelen = valuelen;
452 args.value = kmem_alloc(valuelen, KM_SLEEP | KM_NOFS);
453 args.rmtblkno = be32_to_cpu(name_rmt->valueblk);
454 args.rmtblkcnt = xfs_attr3_rmt_blocks(
455 args.dp->i_mount, valuelen);
456 retval = xfs_attr_rmtval_get(&args);
457 if (retval)
458 return retval;
459 retval = context->put_listent(context,
460 entry->flags,
461 name_rmt->name,
462 (int)name_rmt->namelen,
463 valuelen,
464 args.value);
465 kmem_free(args.value);
466 } else {
467 retval = context->put_listent(context,
468 entry->flags,
469 name_rmt->name,
470 (int)name_rmt->namelen,
471 valuelen,
472 NULL);
473 }
474 if (retval)
475 return retval;
476 }
477 if (context->seen_enough)
478 break;
479 cursor->offset++;
480 }
481 trace_xfs_attr_list_leaf_end(context);
482 return retval;
483}
484
485/*
486 * Copy out attribute entries for attr_list(), for leaf attribute lists.
487 */
488STATIC int
489xfs_attr_leaf_list(xfs_attr_list_context_t *context)
490{
491 int error;
492 struct xfs_buf *bp;
493
494 trace_xfs_attr_leaf_list(context);
495
496 context->cursor->blkno = 0;
497 error = xfs_attr3_leaf_read(NULL, context->dp, 0, -1, &bp);
498 if (error)
499 return XFS_ERROR(error);
500
501 error = xfs_attr3_leaf_list_int(bp, context);
502 xfs_trans_brelse(NULL, bp);
503 return XFS_ERROR(error);
504}
505
506int
507xfs_attr_list_int(
508 xfs_attr_list_context_t *context)
509{
510 int error;
511 xfs_inode_t *dp = context->dp;
512
513 XFS_STATS_INC(xs_attr_list);
514
515 if (XFS_FORCED_SHUTDOWN(dp->i_mount))
516 return EIO;
517
518 xfs_ilock(dp, XFS_ILOCK_SHARED);
519
520 /*
521 * Decide on what work routines to call based on the inode size.
522 */
523 if (!xfs_inode_hasattr(dp)) {
524 error = 0;
525 } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
526 error = xfs_attr_shortform_list(context);
527 } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
528 error = xfs_attr_leaf_list(context);
529 } else {
530 error = xfs_attr_node_list(context);
531 }
532
533 xfs_iunlock(dp, XFS_ILOCK_SHARED);
534
535 return error;
536}
537
538#define ATTR_ENTBASESIZE /* minimum bytes used by an attr */ \
539 (((struct attrlist_ent *) 0)->a_name - (char *) 0)
540#define ATTR_ENTSIZE(namelen) /* actual bytes used by an attr */ \
541 ((ATTR_ENTBASESIZE + (namelen) + 1 + sizeof(u_int32_t)-1) \
542 & ~(sizeof(u_int32_t)-1))
543
544/*
545 * Format an attribute and copy it out to the user's buffer.
546 * Take care to check values and protect against them changing later,
547 * we may be reading them directly out of a user buffer.
548 */
549STATIC int
550xfs_attr_put_listent(
551 xfs_attr_list_context_t *context,
552 int flags,
553 unsigned char *name,
554 int namelen,
555 int valuelen,
556 unsigned char *value)
557{
558 struct attrlist *alist = (struct attrlist *)context->alist;
559 attrlist_ent_t *aep;
560 int arraytop;
561
562 ASSERT(!(context->flags & ATTR_KERNOVAL));
563 ASSERT(context->count >= 0);
564 ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
565 ASSERT(context->firstu >= sizeof(*alist));
566 ASSERT(context->firstu <= context->bufsize);
567
568 /*
569 * Only list entries in the right namespace.
570 */
571 if (((context->flags & ATTR_SECURE) == 0) !=
572 ((flags & XFS_ATTR_SECURE) == 0))
573 return 0;
574 if (((context->flags & ATTR_ROOT) == 0) !=
575 ((flags & XFS_ATTR_ROOT) == 0))
576 return 0;
577
578 arraytop = sizeof(*alist) +
579 context->count * sizeof(alist->al_offset[0]);
580 context->firstu -= ATTR_ENTSIZE(namelen);
581 if (context->firstu < arraytop) {
582 trace_xfs_attr_list_full(context);
583 alist->al_more = 1;
584 context->seen_enough = 1;
585 return 1;
586 }
587
588 aep = (attrlist_ent_t *)&context->alist[context->firstu];
589 aep->a_valuelen = valuelen;
590 memcpy(aep->a_name, name, namelen);
591 aep->a_name[namelen] = 0;
592 alist->al_offset[context->count++] = context->firstu;
593 alist->al_count = context->count;
594 trace_xfs_attr_list_add(context);
595 return 0;
596}
597
598/*
599 * Generate a list of extended attribute names and optionally
600 * also value lengths. Positive return value follows the XFS
601 * convention of being an error, zero or negative return code
602 * is the length of the buffer returned (negated), indicating
603 * success.
604 */
605int
606xfs_attr_list(
607 xfs_inode_t *dp,
608 char *buffer,
609 int bufsize,
610 int flags,
611 attrlist_cursor_kern_t *cursor)
612{
613 xfs_attr_list_context_t context;
614 struct attrlist *alist;
615 int error;
616
617 /*
618 * Validate the cursor.
619 */
620 if (cursor->pad1 || cursor->pad2)
621 return(XFS_ERROR(EINVAL));
622 if ((cursor->initted == 0) &&
623 (cursor->hashval || cursor->blkno || cursor->offset))
624 return XFS_ERROR(EINVAL);
625
626 /*
627 * Check for a properly aligned buffer.
628 */
629 if (((long)buffer) & (sizeof(int)-1))
630 return XFS_ERROR(EFAULT);
631 if (flags & ATTR_KERNOVAL)
632 bufsize = 0;
633
634 /*
635 * Initialize the output buffer.
636 */
637 memset(&context, 0, sizeof(context));
638 context.dp = dp;
639 context.cursor = cursor;
640 context.resynch = 1;
641 context.flags = flags;
642 context.alist = buffer;
643 context.bufsize = (bufsize & ~(sizeof(int)-1)); /* align */
644 context.firstu = context.bufsize;
645 context.put_listent = xfs_attr_put_listent;
646
647 alist = (struct attrlist *)context.alist;
648 alist->al_count = 0;
649 alist->al_more = 0;
650 alist->al_offset[0] = context.bufsize;
651
652 error = xfs_attr_list_int(&context);
653 ASSERT(error >= 0);
654 return error;
655}