diff --git a/fs/apfs/Makefile b/fs/apfs/Makefile index ee4dabbabb35..ebf7e2d37adb 100644 --- a/fs/apfs/Makefile +++ b/fs/apfs/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_APFS_FS) += apfs.o apfs-y := btree.o dir.o extents.o file.o inode.o key.o message.o namei.o \ node.o object.o spaceman.o super.o symlink.o transaction.o \ - unicode.o xattr.o + unicode.o xattr.o xfield.o diff --git a/fs/apfs/dir.c b/fs/apfs/dir.c index 0cd1cdd12581..eacc98c5e628 100644 --- a/fs/apfs/dir.c +++ b/fs/apfs/dir.c @@ -16,6 +16,7 @@ #include "node.h" #include "super.h" #include "transaction.h" +#include "xfield.h" /** * apfs_drec_from_query - Read the directory record found by a successful query @@ -36,6 +37,8 @@ static int apfs_drec_from_query(struct apfs_query *query, struct apfs_drec_hashed_key *de_key; struct apfs_drec_val *de; int namelen = query->key_len - sizeof(*de_key); + char *xval = NULL; + int xlen; if (namelen < 1) return -EFSCORRUPTED; @@ -54,18 +57,12 @@ static int apfs_drec_from_query(struct apfs_query *query, return -EFSCORRUPTED; /* The dentry may have at most one xfield: the sibling id */ - if (query->len == sizeof(*de)) { - drec->sibling_id = 0; - } else { - int x_meta_len; - __le64 *sib_id; + drec->sibling_id = 0; + xlen = apfs_find_xfield(de->xfields, query->len - sizeof(*de), + APFS_DREC_EXT_TYPE_SIBLING_ID, &xval); + if (xlen >= sizeof(__le64)) { + __le64 *sib_id = (__le64 *)xval; - x_meta_len = sizeof(struct apfs_xf_blob) + - sizeof(struct apfs_x_field); - if (query->len != sizeof(*de) + x_meta_len + sizeof(*sib_id)) - return -EFSCORRUPTED; - - sib_id = (void *)de->xfields + x_meta_len; drec->sibling_id = le64_to_cpup(sib_id); } diff --git a/fs/apfs/inode.c b/fs/apfs/inode.c index 0b0793f96f81..7dad3b515a8e 100644 --- a/fs/apfs/inode.c +++ b/fs/apfs/inode.c @@ -18,6 +18,7 @@ #include "node.h" #include "super.h" #include "xattr.h" +#include "xfield.h" static int apfs_readpage(struct file *file, struct page *page) { @@ -84,11 +85,9 @@ static int apfs_inode_from_query(struct apfs_query *query, struct inode *inode) { struct apfs_inode_info *ai = APFS_I(inode); struct apfs_inode_val *inode_val; - struct apfs_dstream *dstream = NULL; - struct apfs_xf_blob *xblob; - struct apfs_x_field *xfield; char *raw = query->node->object.bh->b_data; - int rest, i; + char *xval = NULL; + int xlen; u32 rdev = 0; if (query->len < sizeof(*inode_val)) @@ -121,45 +120,26 @@ static int apfs_inode_from_query(struct apfs_query *query, struct inode *inode) inode->i_mtime = apfs_timespec(inode_val->mod_time); ai->i_crtime = apfs_timespec(inode_val->create_time); - xblob = (struct apfs_xf_blob *) inode_val->xfields; - xfield = (struct apfs_x_field *) xblob->xf_data; - rest = query->len - (sizeof(*inode_val) + sizeof(*xblob)); - rest -= le16_to_cpu(xblob->xf_num_exts) * sizeof(xfield[0]); - if (rest < 0) - goto corrupted; - for (i = 0; i < le16_to_cpu(xblob->xf_num_exts); ++i) { - int attrlen; + inode->i_size = inode->i_blocks = 0; /* TODO: compressed inodes */ + xlen = apfs_find_xfield(inode_val->xfields, + query->len - sizeof(*inode_val), + APFS_INO_EXT_TYPE_DSTREAM, &xval); + if (xlen >= sizeof(struct apfs_dstream)) { + struct apfs_dstream *dstream = (struct apfs_dstream *)xval; - /* Attribute length is padded to a multiple of 8 */ - attrlen = round_up(le16_to_cpu(xfield[i].x_size), 8); - if (attrlen > rest) - break; - - /* These are the only xfields we care about, for now */ - if (xfield[i].x_type == APFS_INO_EXT_TYPE_DSTREAM) { - dstream = (struct apfs_dstream *) - ((char *)inode_val + query->len - rest); - break; - } - if (xfield[i].x_type == APFS_INO_EXT_TYPE_RDEV) { - __le32 *rdev_p = (void *)inode_val + query->len - rest; - - rdev = le32_to_cpu(*rdev_p); - } - - rest -= attrlen; - } - - if (dstream) { inode->i_size = le64_to_cpu(dstream->size); inode->i_blocks = le64_to_cpu(dstream->alloced_size) >> 9; - } else { - /* - * This inode is "empty", but it may actually hold compressed - * data in the named attribute com.apple.decmpfs, and sometimes - * in com.apple.ResourceFork - */ - inode->i_size = inode->i_blocks = 0; + } + xval = NULL; + + rdev = 0; + xlen = apfs_find_xfield(inode_val->xfields, + query->len - sizeof(*inode_val), + APFS_INO_EXT_TYPE_RDEV, &xval); + if (xlen >= sizeof(__le32)) { + __le32 *rdev_p = (__le32 *)xval; + + rdev = le32_to_cpup(rdev_p); } apfs_inode_set_ops(inode, rdev); diff --git a/fs/apfs/inode.h b/fs/apfs/inode.h index eb2e6951acc1..36e33ea57dc1 100644 --- a/fs/apfs/inode.h +++ b/fs/apfs/inode.h @@ -49,52 +49,6 @@ struct apfs_inode_val { /*5C*/ u8 xfields[]; } __packed; -/* Extended field types */ -#define APFS_DREC_EXT_TYPE_SIBLING_ID 1 - -#define APFS_INO_EXT_TYPE_SNAP_XID 1 -#define APFS_INO_EXT_TYPE_DELTA_TREE_OID 2 -#define APFS_INO_EXT_TYPE_DOCUMENT_ID 3 -#define APFS_INO_EXT_TYPE_NAME 4 -#define APFS_INO_EXT_TYPE_PREV_FSIZE 5 -#define APFS_INO_EXT_TYPE_RESERVED_6 6 -#define APFS_INO_EXT_TYPE_FINDER_INFO 7 -#define APFS_INO_EXT_TYPE_DSTREAM 8 -#define APFS_INO_EXT_TYPE_RESERVED_9 9 -#define APFS_INO_EXT_TYPE_DIR_STATS_KEY 10 -#define APFS_INO_EXT_TYPE_FS_UUID 11 -#define APFS_INO_EXT_TYPE_RESERVED_12 12 -#define APFS_INO_EXT_TYPE_SPARSE_BYTES 13 -#define APFS_INO_EXT_TYPE_RDEV 14 - -/* Extended field flags */ -#define APFS_XF_DATA_DEPENDENT 0x01 -#define APFS_XF_DO_NOT_COPY 0x02 -#define APFS_XF_RESERVED_4 0x04 -#define APFS_XF_CHILDREN_INHERIT 0x08 -#define APFS_XF_USER_FIELD 0x10 -#define APFS_XF_SYSTEM_FIELD 0x20 -#define APFS_XF_RESERVED_40 0x40 -#define APFS_XF_RESERVED_80 0x80 - -/* - * Structure used to store the number and size of extended fields of an inode - */ -struct apfs_xf_blob { - __le16 xf_num_exts; - __le16 xf_used_data; - u8 xf_data[]; -} __packed; - -/* - * Structure used to store an inode's extended field - */ -struct apfs_x_field { - u8 x_type; - u8 x_flags; - __le16 x_size; -} __packed; - /* * Structure of a data stream record */ diff --git a/fs/apfs/xfield.c b/fs/apfs/xfield.c new file mode 100644 index 000000000000..0913f40ebdc0 --- /dev/null +++ b/fs/apfs/xfield.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/xfield.c + * + * Copyright (C) 2019 Ernesto A. Fernández + */ + +#include +#include "xfield.h" + +/** + * apfs_find_xfield - Find an extended field value in an inode or dentry record + * @xfields: pointer to the on-disk xfield collection for the record + * @len: length of the collection + * @xtype: type of the xfield to retrieve + * @xval: on return, a pointer to the wanted on-disk xfield value + * + * Returns the length of @xval on success, or 0 if no matching xfield was found; + * the caller must check that the expected structures fit before casting @xval. + */ +int apfs_find_xfield(u8 *xfields, int len, u8 xtype, char **xval) +{ + struct apfs_xf_blob *blob; + struct apfs_x_field *xfield; + int count; + int rest = len; + int i; + + if (!len) + return 0; /* No xfield data */ + + rest -= sizeof(*blob); + if (rest < 0) + return 0; /* Corruption */ + blob = (struct apfs_xf_blob *)xfields; + + count = le16_to_cpu(blob->xf_num_exts); + rest -= count * sizeof(*xfield); + if (rest < 0) + return 0; /* Corruption */ + xfield = (struct apfs_x_field *)blob->xf_data; + + for (i = 0; i < count; ++i) { + int xlen; + + /* Attribute length is padded to a multiple of 8 */ + xlen = round_up(le16_to_cpu(xfield[i].x_size), 8); + if (xlen > rest) + return 0; /* Corruption */ + + if (xfield[i].x_type == xtype) { + *xval = (char *)xfields + len - rest; + return xlen; + } + rest -= xlen; + } + return 0; +} diff --git a/fs/apfs/xfield.h b/fs/apfs/xfield.h new file mode 100644 index 000000000000..023f7c6db88d --- /dev/null +++ b/fs/apfs/xfield.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/xfield.h + * + * Copyright (C) 2019 Ernesto A. Fernández + */ + +#ifndef _APFS_XFIELD_H +#define _APFS_XFIELD_H + +#include + +/* Extended field types for dentries */ +#define APFS_DREC_EXT_TYPE_SIBLING_ID 1 + +/* Extended field types for inodes */ +#define APFS_INO_EXT_TYPE_SNAP_XID 1 +#define APFS_INO_EXT_TYPE_DELTA_TREE_OID 2 +#define APFS_INO_EXT_TYPE_DOCUMENT_ID 3 +#define APFS_INO_EXT_TYPE_NAME 4 +#define APFS_INO_EXT_TYPE_PREV_FSIZE 5 +#define APFS_INO_EXT_TYPE_RESERVED_6 6 +#define APFS_INO_EXT_TYPE_FINDER_INFO 7 +#define APFS_INO_EXT_TYPE_DSTREAM 8 +#define APFS_INO_EXT_TYPE_RESERVED_9 9 +#define APFS_INO_EXT_TYPE_DIR_STATS_KEY 10 +#define APFS_INO_EXT_TYPE_FS_UUID 11 +#define APFS_INO_EXT_TYPE_RESERVED_12 12 +#define APFS_INO_EXT_TYPE_SPARSE_BYTES 13 +#define APFS_INO_EXT_TYPE_RDEV 14 + +/* Extended field flags */ +#define APFS_XF_DATA_DEPENDENT 0x01 +#define APFS_XF_DO_NOT_COPY 0x02 +#define APFS_XF_RESERVED_4 0x04 +#define APFS_XF_CHILDREN_INHERIT 0x08 +#define APFS_XF_USER_FIELD 0x10 +#define APFS_XF_SYSTEM_FIELD 0x20 +#define APFS_XF_RESERVED_40 0x40 +#define APFS_XF_RESERVED_80 0x80 + +/* + * Structure used to store the number and size of an xfield collection + */ +struct apfs_xf_blob { + __le16 xf_num_exts; + __le16 xf_used_data; + u8 xf_data[]; +} __packed; + +/* + * Structure used to describe an extended field + */ +struct apfs_x_field { + u8 x_type; + u8 x_flags; + __le16 x_size; +} __packed; + +extern int apfs_find_xfield(u8 *xfields, int len, u8 xtype, char **xval); + +#endif /* _APFS_XFIELD_H */