Files
Ernesto A. Fernández dc1ed2cfe7 Rename SPDX license identifier
It seems that the license identifier currently in use (GPL-2.0) has been
deprecated:

  https://spdx.org/licenses/GPL-2.0.html

I don't know how important this is in practice, but I've received some
complaints about it:

  https://github.com/linux-apfs/linux-apfs-rw/issues/18

So, just run

  sed -i 's/2\.0/2.0-only/' *.{c,h}
  sed -i 's/2\.0/2.0-only/' Makefile

and change it to GPL-2.0-only.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2021-11-16 18:41:54 -03:00

172 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include "apfs.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;
}
/**
* apfs_init_xfields - Set an empty collection of xfields in a buffer
* @buffer: buffer to hold the xfields
* @buflen: length of the buffer; should be enough to fit an xfield blob
*
* Returns 0 on success, or -1 if the buffer isn't long enough.
*/
int apfs_init_xfields(u8 *buffer, int buflen)
{
struct apfs_xf_blob *blob;
if (buflen < sizeof(*blob))
return -1;
blob = (struct apfs_xf_blob *)buffer;
blob->xf_num_exts = 0;
blob->xf_used_data = 0;
return 0;
}
/**
* apfs_insert_xfield - Add a new xfield to an in-memory collection
* @buffer: buffer holding the collection of xfields
* @buflen: length of the buffer; should be enough to fit the new xfield
* @xkey: metadata for the new xfield
* @xval: value for the new xfield
*
* Returns the new length of the collection, or 0 if it the allocation would
* overflow @buffer.
*/
int apfs_insert_xfield(u8 *buffer, int buflen, const struct apfs_x_field *xkey,
const void *xval)
{
struct apfs_xf_blob *blob;
struct apfs_x_field *curr_xkey;
void *curr_xval;
int count;
int rest = buflen;
u16 used_data;
int xlen, padded_xlen;
int meta_len, total_len;
int i;
xlen = le16_to_cpu(xkey->x_size);
padded_xlen = round_up(xlen, 8);
if (!buflen)
return 0;
rest -= sizeof(*blob);
if (rest < 0)
return 0;
blob = (struct apfs_xf_blob *)buffer;
used_data = le16_to_cpu(blob->xf_used_data);
count = le16_to_cpu(blob->xf_num_exts);
rest -= count * sizeof(*curr_xkey);
if (rest < 0)
return 0;
meta_len = buflen - rest;
curr_xkey = (struct apfs_x_field *)blob->xf_data;
for (i = 0; i < count; ++i, ++curr_xkey) {
int curr_xlen;
/* Attribute length is padded to a multiple of 8 */
curr_xlen = round_up(le16_to_cpu(curr_xkey->x_size), 8);
if (curr_xlen > rest)
return 0;
if (curr_xkey->x_type != xkey->x_type) {
rest -= curr_xlen;
continue;
}
/* The xfield already exists, so just resize it and set it */
memcpy(curr_xkey, xkey, sizeof(*curr_xkey));
if (padded_xlen > rest)
return 0;
curr_xval = buffer + buflen - rest;
rest -= max(padded_xlen, curr_xlen);
memmove(curr_xval + padded_xlen, curr_xval + curr_xlen, rest);
memcpy(curr_xval, xval, xlen);
memset(curr_xval + xlen, 0, padded_xlen - xlen);
used_data += padded_xlen - curr_xlen;
goto done;
}
/* Create a metadata entry for the new xfield */
rest -= sizeof(*curr_xkey);
if (rest < 0)
return 0;
meta_len += sizeof(*curr_xkey);
memmove(curr_xkey + 1, curr_xkey, buflen - meta_len);
memcpy(curr_xkey, xkey, sizeof(*curr_xkey));
++count;
/* Now set the xfield value */
if (padded_xlen > rest)
return 0;
curr_xval = buffer + buflen - rest;
memcpy(curr_xval, xval, xlen);
memset(curr_xval + xlen, 0, padded_xlen - xlen);
used_data += padded_xlen;
done:
total_len = used_data + meta_len;
if (total_len > buflen)
return 0;
blob->xf_num_exts = cpu_to_le16(count);
blob->xf_used_data = cpu_to_le16(used_data);
return total_len;
}