summaryrefslogtreecommitdiffstats
path: root/fs/udf/misc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-23 13:28:57 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-23 13:28:57 -0700
commit4352b8aee98005853aa63f57d6377282de17a33f (patch)
tree27614d7a9ef05c32d089004e104364ce42324837 /fs/udf/misc.c
downloadlinux-stable-4352b8aee98005853aa63f57d6377282de17a33f.tar.gz
linux-stable-4352b8aee98005853aa63f57d6377282de17a33f.zip
Merge tag 'i3c/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linuxgrafted
Pull I3C updates from Alexandre Belloni: "A new driver this cycle, for the AMD AXI I3C controller. There are also new features such as IBI wakeup support, SETAASA device discovery and ACPI support for the the DAA methods, meaning we can now communicate with the SPD devices on DDR5 modules. New driver: - AMD AXI I3C master controller Subsystem: - I3C Common Command Code (CCC) handling improvements, especially around GET CCCs - SETAASA device discovery support - ACPI support for all existing DAA methods like SETDASA, SETNEWDA as well as I2C devices on I3C bus - IBI-based wakeup support Drivers: - dw: SETAASA support - mipi-i3c-hci: advertise IBI wakeup capability, AMD_PT I3C controller support, PIO queue management support for HCI v1.2 - renesas: improve suspend to RAM support, add runtime PM support" * tag 'i3c/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (70 commits) i3c: dw: reduce do_daa time if there's no client i3c: mipi-i3c-hci: Add support for AMD_PT I3C controller i3c: mipi-i3c-hci: Add PIO queue management support for HCI v1.2 i3c: mipi-i3c-hci: Fix missing STAT_IBI_STATUS_THLD in PIO mode i3c: dw: rename "pclk" to "apb" to match dt-binding i3c: dw: make struct dw_i3c_cmd smaller i3c: dw: use COMMAND_PORT_TRANSFER_ARG instead of hardcoding i3c: renesas: Don't register devices when ENTDAA times out i3c: master: dw-i3c-master: fix OD timing for first broadcast i3c: mipi-i3c-hci: Advertise IBI wakeup capability i3c: mipi-i3c-hci: Factor out i3c_hci_sysdev() i3c: mipi-i3c-hci-pci: Propagate I3C wakeup requirements to PCI i3c: master: Reject IBI requests from non-IBI-capable devices i3c: master: Add helper to query bus wakeup requirements i3c: master: Report wakeup events for IBIs i3c: master: Support IBI-based wakeup capability i3c: master: Fix potential UAF in i3c_device_match() i3c: master: Fix potential UAF in i3c_device_uevent() i3c: Make dev->desc locking assumptions explicit i3c: master: Fix use-after-free of master->this ...
Diffstat (limited to 'fs/udf/misc.c')
-rw-r--r--fs/udf/misc.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/fs/udf/misc.c b/fs/udf/misc.c
new file mode 100644
index 000000000..6928e378f
--- /dev/null
+++ b/fs/udf/misc.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * misc.c
+ *
+ * PURPOSE
+ * Miscellaneous routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ * (C) 1998 Dave Boynton
+ * (C) 1998-2004 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 04/19/99 blf partial support for reading/writing specific EA's
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/crc-itu-t.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
+ uint32_t type, uint8_t loc)
+{
+ uint8_t *ea = NULL, *ad = NULL;
+ int offset;
+ uint16_t crclen;
+ struct udf_inode_info *iinfo = UDF_I(inode);
+
+ ea = iinfo->i_data;
+ if (iinfo->i_lenEAttr) {
+ ad = iinfo->i_data + iinfo->i_lenEAttr;
+ } else {
+ ad = ea;
+ size += sizeof(struct extendedAttrHeaderDesc);
+ }
+
+ offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) -
+ iinfo->i_lenAlloc;
+
+ /* TODO - Check for FreeEASpace */
+
+ if (loc & 0x01 && offset >= size) {
+ struct extendedAttrHeaderDesc *eahd;
+ eahd = (struct extendedAttrHeaderDesc *)ea;
+
+ if (iinfo->i_lenAlloc)
+ memmove(&ad[size], ad, iinfo->i_lenAlloc);
+
+ if (iinfo->i_lenEAttr) {
+ /* check checksum/crc */
+ if (eahd->descTag.tagIdent !=
+ cpu_to_le16(TAG_IDENT_EAHD) ||
+ le32_to_cpu(eahd->descTag.tagLocation) !=
+ iinfo->i_location.logicalBlockNum)
+ return NULL;
+ } else {
+ struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
+
+ size -= sizeof(struct extendedAttrHeaderDesc);
+ iinfo->i_lenEAttr +=
+ sizeof(struct extendedAttrHeaderDesc);
+ eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
+ if (sbi->s_udfrev >= 0x0200)
+ eahd->descTag.descVersion = cpu_to_le16(3);
+ else
+ eahd->descTag.descVersion = cpu_to_le16(2);
+ eahd->descTag.tagSerialNum =
+ cpu_to_le16(sbi->s_serial_number);
+ eahd->descTag.tagLocation = cpu_to_le32(
+ iinfo->i_location.logicalBlockNum);
+ eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
+ eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
+ }
+
+ offset = iinfo->i_lenEAttr;
+ if (type < 2048) {
+ if (le32_to_cpu(eahd->appAttrLocation) <
+ iinfo->i_lenEAttr) {
+ uint32_t aal =
+ le32_to_cpu(eahd->appAttrLocation);
+ memmove(&ea[offset - aal + size],
+ &ea[aal], offset - aal);
+ offset -= aal;
+ eahd->appAttrLocation =
+ cpu_to_le32(aal + size);
+ }
+ if (le32_to_cpu(eahd->impAttrLocation) <
+ iinfo->i_lenEAttr) {
+ uint32_t ial =
+ le32_to_cpu(eahd->impAttrLocation);
+ memmove(&ea[offset - ial + size],
+ &ea[ial], offset - ial);
+ offset -= ial;
+ eahd->impAttrLocation =
+ cpu_to_le32(ial + size);
+ }
+ } else if (type < 65536) {
+ if (le32_to_cpu(eahd->appAttrLocation) <
+ iinfo->i_lenEAttr) {
+ uint32_t aal =
+ le32_to_cpu(eahd->appAttrLocation);
+ memmove(&ea[offset - aal + size],
+ &ea[aal], offset - aal);
+ offset -= aal;
+ eahd->appAttrLocation =
+ cpu_to_le32(aal + size);
+ }
+ }
+ /* rewrite CRC + checksum of eahd */
+ crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(struct tag);
+ eahd->descTag.descCRCLength = cpu_to_le16(crclen);
+ eahd->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)eahd +
+ sizeof(struct tag), crclen));
+ eahd->descTag.tagChecksum = udf_tag_checksum(&eahd->descTag);
+ iinfo->i_lenEAttr += size;
+ return (struct genericFormat *)&ea[offset];
+ }
+
+ return NULL;
+}
+
+struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type,
+ uint8_t subtype)
+{
+ struct genericFormat *gaf;
+ uint8_t *ea = NULL;
+ uint32_t offset;
+ struct udf_inode_info *iinfo = UDF_I(inode);
+
+ ea = iinfo->i_data;
+
+ if (iinfo->i_lenEAttr) {
+ struct extendedAttrHeaderDesc *eahd;
+ eahd = (struct extendedAttrHeaderDesc *)ea;
+
+ /* check checksum/crc */
+ if (eahd->descTag.tagIdent !=
+ cpu_to_le16(TAG_IDENT_EAHD) ||
+ le32_to_cpu(eahd->descTag.tagLocation) !=
+ iinfo->i_location.logicalBlockNum)
+ return NULL;
+
+ if (type < 2048)
+ offset = sizeof(struct extendedAttrHeaderDesc);
+ else if (type < 65536)
+ offset = le32_to_cpu(eahd->impAttrLocation);
+ else
+ offset = le32_to_cpu(eahd->appAttrLocation);
+
+ while (offset + sizeof(*gaf) < iinfo->i_lenEAttr) {
+ uint32_t attrLength;
+
+ gaf = (struct genericFormat *)&ea[offset];
+ attrLength = le32_to_cpu(gaf->attrLength);
+
+ /* Detect undersized elements and buffer overflows */
+ if ((attrLength < sizeof(*gaf)) ||
+ (attrLength > (iinfo->i_lenEAttr - offset)))
+ break;
+
+ if (le32_to_cpu(gaf->attrType) == type &&
+ gaf->attrSubtype == subtype)
+ return gaf;
+ else
+ offset += attrLength;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * udf_read_tagged
+ *
+ * PURPOSE
+ * Read the first block of a tagged descriptor.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block,
+ uint32_t location, uint16_t *ident)
+{
+ struct tag *tag_p;
+ struct buffer_head *bh = NULL;
+ u8 checksum;
+
+ /* Read the block */
+ if (block == 0xFFFFFFFF)
+ return NULL;
+
+ bh = sb_bread(sb, block);
+ if (!bh) {
+ udf_err(sb, "read failed, block=%u, location=%u\n",
+ block, location);
+ return NULL;
+ }
+
+ tag_p = (struct tag *)(bh->b_data);
+
+ *ident = le16_to_cpu(tag_p->tagIdent);
+
+ if (location != le32_to_cpu(tag_p->tagLocation)) {
+ udf_debug("location mismatch block %u, tag %u != %u\n",
+ block, le32_to_cpu(tag_p->tagLocation), location);
+ goto error_out;
+ }
+
+ /* Verify the tag checksum */
+ checksum = udf_tag_checksum(tag_p);
+ if (checksum != tag_p->tagChecksum) {
+ udf_err(sb, "tag checksum failed, block %u: 0x%02x != 0x%02x\n",
+ block, checksum, tag_p->tagChecksum);
+ goto error_out;
+ }
+
+ /* Verify the tag version */
+ if (tag_p->descVersion != cpu_to_le16(0x0002U) &&
+ tag_p->descVersion != cpu_to_le16(0x0003U)) {
+ udf_err(sb, "tag version 0x%04x != 0x0002 || 0x0003, block %u\n",
+ le16_to_cpu(tag_p->descVersion), block);
+ goto error_out;
+ }
+
+ /* Verify the descriptor CRC */
+ if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize) {
+ udf_err(sb, "block %u: CRC length %u exceeds block size\n",
+ block, le16_to_cpu(tag_p->descCRCLength));
+ goto error_out;
+ }
+ if (le16_to_cpu(tag_p->descCRC) == crc_itu_t(0,
+ bh->b_data + sizeof(struct tag),
+ le16_to_cpu(tag_p->descCRCLength)))
+ return bh;
+
+ udf_debug("Crc failure block %u: crc = %u, crclen = %u\n", block,
+ le16_to_cpu(tag_p->descCRC),
+ le16_to_cpu(tag_p->descCRCLength));
+error_out:
+ brelse(bh);
+ return NULL;
+}
+
+struct buffer_head *udf_read_ptagged(struct super_block *sb,
+ struct kernel_lb_addr *loc,
+ uint32_t offset, uint16_t *ident)
+{
+ return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset),
+ loc->logicalBlockNum + offset, ident);
+}
+
+void udf_update_tag(char *data, int length)
+{
+ struct tag *tptr = (struct tag *)data;
+ length -= sizeof(struct tag);
+
+ tptr->descCRCLength = cpu_to_le16(length);
+ tptr->descCRC = cpu_to_le16(crc_itu_t(0, data + sizeof(struct tag), length));
+ tptr->tagChecksum = udf_tag_checksum(tptr);
+}
+
+void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
+ uint32_t loc, int length)
+{
+ struct tag *tptr = (struct tag *)data;
+ tptr->tagIdent = cpu_to_le16(ident);
+ tptr->descVersion = cpu_to_le16(version);
+ tptr->tagSerialNum = cpu_to_le16(snum);
+ tptr->tagLocation = cpu_to_le32(loc);
+ udf_update_tag(data, length);
+}
+
+u8 udf_tag_checksum(const struct tag *t)
+{
+ u8 *data = (u8 *)t;
+ u8 checksum = 0;
+ int i;
+ for (i = 0; i < sizeof(struct tag); ++i)
+ if (i != 4) /* position of checksum */
+ checksum += data[i];
+ return checksum;
+}