Figure 116 Code flow diagram for ext3xattribodyget

After the location of the inode is determined and access to the raw data is ascertained, ext3_xattr_check_names performs several sanity checks that ensure that the entry table is located within the free space of the inode. The real work is delegated to ext3_xattr_find_entry. Since the routine will be used on several more occasions further below, we need to discuss it in more detail.

fs/ext3/xattr.c static int ext3_xattr_find_entry(struct ext3_xattr_entry **pentry, int name_index, const char *name, size_t size, int sorted)

struct ext3_xattr_entry *entry; size_t name_len; int cmp = 1;

return -EINVAL; name_len = strlen(name); entry = *pentry;

for (; !IS_LAST_ENTRY(entry); entry = EXT3_XATTR_NEXT(entry)) { cmp = name_index - entry->e_name_index; if (!cmp)

cmp = memcmp(name, entry->e_name, name_len); if (cmp <= 0 && (sorted || cmp == 0)) break;

pentry points to the start of the extended attribute entry table. The code loops over all entries and compares the desired name with the entry name if the entry has the correct type (as indicated by cmp ==0, which results from subtracting the namespace index of the entry under consideration from the index of the queried entry — a slightly unconventional but nevertheless valid way to check this). Since the entries do not have a uniform size, the kernel uses EXT3_XATTR_NEXT to compute the address of the next entry in the table by adding the length of the actual attribute name (plus some padding that is handled by ext3_xattr_len) to the size of the entry data structure:

fs/ext3/xattr.h

#define EXT3_XATTR_NEXT(entry) \

(char *)(entry) + EXT3_XATTR_LEN((entry)->e_name_len)) )

The end of the list is marked by a zero that IS_LAST_ENTRY checks for.

After ext3_xattr_find_entry returns with the data of the desired entry, ext3_xattr_ibody_get needs to copy the value to the buffer given in the function arguments if it is not a NULL pointer; otherwise, only the size of the entry is returned.

If the desired extended attribute cannot be found within the inode, the kernel uses ext3_xattr_block_ get to search for the entry. The associated code flow diagram is presented in Figure 11-7.

Figure 11-7: Code flow diagram for ext3_xattr_block_get.

The course of action is basically identical with the previously considered case where the data were located in the inode, but two modifications need to be made:

□ The kernel needs to read the extended attribute block; the address is stored in the i_file_acl element of struct ext3_inode_info.

□ Metadata blocks are cached by calling ext3_xattr_cache_insert. The kernel uses the so-called filesystem metadata block cache implemented in fs/mbcache.c for this.2 Since nothing really unexpected happens there, it is not necessary to discuss the code in more detail.

Setting Extended Attributes

Setting extended attributes for the user namespace is handled by — you guessed it — ext3_xattr_user_ set. As for the get operation, the function is just a wrapper for the generic helper ext3_xattr_set. The code flow diagram in Figure 11-8 shows that this is yet another wrapper function that is responsible for handling the interaction with the journal. The real work is delegated to ext3_xattr_set_handle; the associated code flow diagram can be seen in Figure 11-9.

ext3_xattr_set

Get handle

ext3_xattr_set_handle|

Stop Journal

Figure 11-8: Code flow diagram for ext3_xattr_set.

Figure 11-8: Code flow diagram for ext3_xattr_set.

Wrapper Code Diagram
Figure 11-9: Code flow diagram for ext3_xattr_set.

2Although the structure of this cache is generic, it is currently only used by the extended filesystem family.

The following calling convention is used:

□ If the data buffer passed to the function is null, then remove an existing extended attribute.

□ If the data buffer contains a value, replace an existing extended attribute or create a new one. The flags xattr_replace and xattr_create can be used to indicate that the attribute must or must not exist before the call as per the documentation in the man page setxattr(2).

ext3_xattr_set_handle implements these requirements by utilizing the previously introduced framework as follows:

1. Find the location of the inode.

2. Use ext3_xattr_ibody_find to find the data of the extended attribute. If this fails, search in the external data block with ext3_xattr_block_find.

3. If no value is given, delete the attribute with ext3_xattr_ibody_set or ext3_xattr_block_ set depending on whether the entry is contained in the inode or in a separate data block.

4. If a value was given, use ext3_xattr_*_set to modify the value or create a new value either within the inode or on the external data block depending on where enough space is left.

The functions ext3_xattr_ibody_set and ext3_xattr_block_set handle the low-level work of removing an entry from the data structure described in Section 11.1.2. If no value is given to update, the functions respectively create a new entry. This is primarily a matter of data structure manipulation and will not be discussed in detail here.

Listing Extended Attributes

Although the kernel includes a generic function (generic_listxattr) for listing all extended attributes associated with a file, it is not among the filesystem favorites: Only the shared memory implementation makes use of it. So let's step back a little farther to discuss the operation for Ext3.

The inode_operations instance for Ext3 lists ext3_listxattr as the handler function for listxattr. The method is just a one-line wrapper for ext3_xattr_list. This routine calls, in turn, ext3_xattr_ibody_list and ext3_xattr_block_list, depending on where extended attributes are stored. Both functions compute the location of the extended attributes and read the data, but then delegate the work to ext3_xattr_list_entries, which finally does the real work — after all, someone has to do it! It uses the previously introduced macros to iterate over all extended attributes defined for the inode, calls handler->list to retrieve the name of the attribute for each entry, and collects the results in a buffer:

fs/ext3/xattr.c static int ext3_xattr_list_entries(struct inode *inode, struct ext3_xattr_entry *entry, char *buffer, size_t buffer_size)

size_t rest = buffer_size;

for (; !IS_LAST_ENTRY(entry); entry = EXT3_XATTR_NEXT(entry)) { struct xattr_handler *handler =

ext3_xattr_handler(entry->e_name_index);

size_t size = handler->list(inode, buffer, rest, entry->e_name, entry->e_name_len);

return -ERANGE; buffer += size;

return buffer_size - rest;

Since the list handler implementation is quite similar for the various attribute types, it suffices to consider the variant for the user namespace. Observe the following code:

fs/ext3/xattr_user.c static size_t ext3_xattr_user_list(struct inode *inode, char *list, size_t list_size, const char *name, size_t name_len)

const size_t prefix_len = sizeof(XATTR_USER_PREFIX)-1; const size_t total_len = prefix_len + name_len + 1;

if (!test_opt(inode->i_sb, XATTR_USER)) return 0;

memcpy(list, XATTR_USER_PREFIX, prefix_len); memcpy(list+prefix_len, name, name_len); list[prefix_len + name_len] = '\0';

return total_len;

The routine copies the prefix "user.'' followed by the attribute name and a null byte into the buffer list and returns the number of copied bytes as result.

Continue reading here: Implementation in Ext2

Was this article helpful?

0 0