2021-11-16 18:36:02 -03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2021-03-31 17:16:24 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/crc32c.h>
|
|
|
|
|
#include "apfs.h"
|
|
|
|
|
#include "unicode.h"
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_filename_cmp - Normalize and compare two APFS filenames
|
2022-03-01 19:11:15 -03:00
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @name1: first name to compare
|
|
|
|
|
* @len1: length of @name1
|
|
|
|
|
* @name2: second name to compare
|
|
|
|
|
* @len2: length of the @name2
|
2021-03-31 17:16:24 -03:00
|
|
|
*
|
2021-11-09 17:32:37 -03:00
|
|
|
* Returns 0 if @name1 and @name2 are equal, or non-zero otherwise.
|
2021-03-31 17:16:24 -03:00
|
|
|
*/
|
|
|
|
|
int apfs_filename_cmp(struct super_block *sb,
|
2021-11-09 17:32:37 -03:00
|
|
|
const char *name1, unsigned int len1,
|
|
|
|
|
const char *name2, unsigned int len2)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
|
|
|
|
struct apfs_unicursor cursor1, cursor2;
|
|
|
|
|
bool case_fold = apfs_is_case_insensitive(sb);
|
|
|
|
|
|
2021-11-09 17:32:37 -03:00
|
|
|
if (!apfs_is_normalization_insensitive(sb)) {
|
|
|
|
|
if (len1 != len2)
|
|
|
|
|
return -1;
|
|
|
|
|
return memcmp(name1, name2, len1);
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2021-11-09 17:32:37 -03:00
|
|
|
apfs_init_unicursor(&cursor1, name1, len1);
|
|
|
|
|
apfs_init_unicursor(&cursor2, name2, len2);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
unicode_t uni1, uni2;
|
|
|
|
|
|
|
|
|
|
uni1 = apfs_normalize_next(&cursor1, case_fold);
|
|
|
|
|
uni2 = apfs_normalize_next(&cursor2, case_fold);
|
|
|
|
|
|
|
|
|
|
if (uni1 != uni2)
|
|
|
|
|
return uni1 < uni2 ? -1 : 1;
|
|
|
|
|
if (!uni1)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_keycmp - Compare two keys
|
2022-03-01 19:11:15 -03:00
|
|
|
* @k1: first key to compare
|
|
|
|
|
* @k2: second key to compare
|
2021-03-31 17:16:24 -03:00
|
|
|
*
|
|
|
|
|
* returns 0 if @k1 and @k2 are equal
|
|
|
|
|
* < 0 if @k1 comes before @k2 in the btree
|
|
|
|
|
* > 0 if @k1 comes after @k2 in the btree
|
|
|
|
|
*/
|
2022-03-01 21:47:41 -03:00
|
|
|
int apfs_keycmp(struct apfs_key *k1, struct apfs_key *k2)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
|
|
|
|
if (k1->id != k2->id)
|
|
|
|
|
return k1->id < k2->id ? -1 : 1;
|
|
|
|
|
if (k1->type != k2->type)
|
|
|
|
|
return k1->type < k2->type ? -1 : 1;
|
|
|
|
|
if (k1->number != k2->number)
|
|
|
|
|
return k1->number < k2->number ? -1 : 1;
|
2021-04-17 05:29:10 -03:00
|
|
|
if (!k1->name || !k2->name)
|
2021-03-31 17:16:24 -03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Normalization seems to be ignored here, even for directory records */
|
|
|
|
|
return strcmp(k1->name, k2->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_read_cat_key - Parse an on-disk catalog key
|
|
|
|
|
* @raw: pointer to the raw key
|
|
|
|
|
* @size: size of the raw key
|
|
|
|
|
* @key: apfs_key structure to store the result
|
|
|
|
|
* @hashed: are the directory records hashed?
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code otherwise.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
|
|
|
|
{
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size < sizeof(struct apfs_key_header)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
|
|
|
|
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
|
|
|
|
|
|
|
|
|
switch (key->type) {
|
|
|
|
|
case APFS_TYPE_DIR_REC:
|
|
|
|
|
if (hashed) {
|
|
|
|
|
if (size < sizeof(struct apfs_drec_hashed_key) + 1 ||
|
|
|
|
|
*((char *)raw + size - 1) != 0) {
|
|
|
|
|
/* Filename must have NULL-termination */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "invalid drec key (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
/* Name length is not used in key comparisons, only the hash */
|
|
|
|
|
key->number = le32_to_cpu(
|
|
|
|
|
((struct apfs_drec_hashed_key *)raw)->name_len_and_hash) &
|
|
|
|
|
APFS_DREC_HASH_MASK;
|
|
|
|
|
key->name = ((struct apfs_drec_hashed_key *)raw)->name;
|
|
|
|
|
} else {
|
|
|
|
|
if (size < sizeof(struct apfs_drec_key) + 1 ||
|
|
|
|
|
*((char *)raw + size - 1) != 0) {
|
|
|
|
|
/* Filename must have NULL-termination */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "invalid drec key (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
/* There's no hash */
|
|
|
|
|
key->number = 0;
|
|
|
|
|
key->name = ((struct apfs_drec_key *)raw)->name;
|
|
|
|
|
}
|
2021-04-06 23:24:33 -03:00
|
|
|
break;
|
2021-03-31 17:16:24 -03:00
|
|
|
case APFS_TYPE_XATTR:
|
|
|
|
|
if (size < sizeof(struct apfs_xattr_key) + 1 ||
|
|
|
|
|
*((char *)raw + size - 1) != 0) {
|
|
|
|
|
/* xattr name must have NULL-termination */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "invalid xattr key (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
key->number = 0;
|
|
|
|
|
key->name = ((struct apfs_xattr_key *)raw)->name;
|
|
|
|
|
break;
|
|
|
|
|
case APFS_TYPE_FILE_EXTENT:
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(struct apfs_file_extent_key)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
key->number = le64_to_cpu(
|
|
|
|
|
((struct apfs_file_extent_key *)raw)->logical_addr);
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
break;
|
|
|
|
|
case APFS_TYPE_SIBLING_LINK:
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(struct apfs_sibling_link_key)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
key->number = le64_to_cpu(
|
|
|
|
|
((struct apfs_sibling_link_key *)raw)->sibling_id);
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
key->number = 0;
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-13 23:12:19 -03:00
|
|
|
int apfs_read_fext_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_fext_tree_key *raw_key;
|
|
|
|
|
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(*raw_key)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2023-01-13 23:12:19 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2023-01-13 23:12:19 -03:00
|
|
|
raw_key = raw;
|
|
|
|
|
|
|
|
|
|
key->id = le64_to_cpu(raw_key->private_id);
|
|
|
|
|
key->type = 0;
|
|
|
|
|
key->number = le64_to_cpu(raw_key->logical_addr);
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_read_free_queue_key - Parse an on-disk free queue key
|
|
|
|
|
* @raw: pointer to the raw key
|
|
|
|
|
* @size: size of the raw key
|
|
|
|
|
* @key: apfs_key structure to store the result
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code otherwise.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_spaceman_free_queue_key *raw_key;
|
|
|
|
|
|
2024-12-04 18:11:43 -03:00
|
|
|
if (size != sizeof(*raw_key)) {
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
raw_key = raw;
|
|
|
|
|
|
|
|
|
|
key->id = le64_to_cpu(raw_key->sfqk_xid);
|
|
|
|
|
key->type = 0;
|
|
|
|
|
key->number = le64_to_cpu(raw_key->sfqk_paddr);
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_read_omap_key - Parse an on-disk object map key
|
|
|
|
|
* @raw: pointer to the raw key
|
|
|
|
|
* @size: size of the raw key
|
|
|
|
|
* @key: apfs_key structure to store the result
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code otherwise.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
2024-12-04 18:11:43 -03:00
|
|
|
struct apfs_omap_key *raw_key;
|
|
|
|
|
|
|
|
|
|
if (size != sizeof(*raw_key)) {
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2024-12-04 18:11:43 -03:00
|
|
|
raw_key = raw;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2024-12-04 18:11:43 -03:00
|
|
|
key->id = le64_to_cpu(raw_key->ok_oid);
|
2021-03-31 17:16:24 -03:00
|
|
|
key->type = 0;
|
2024-12-04 18:11:43 -03:00
|
|
|
key->number = le64_to_cpu(raw_key->ok_xid);
|
2021-03-31 17:16:24 -03:00
|
|
|
key->name = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_read_extentref_key - Parse an on-disk extent reference tree key
|
|
|
|
|
* @raw: pointer to the raw key
|
|
|
|
|
* @size: size of the raw key
|
|
|
|
|
* @key: apfs_key structure to store the result
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code otherwise.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(struct apfs_phys_ext_key)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2021-03-31 17:16:24 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
|
|
|
|
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
|
|
|
|
key->number = 0;
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 22:24:32 -03:00
|
|
|
int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size < sizeof(struct apfs_key_header)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2022-09-26 22:24:32 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2022-09-26 22:24:32 -03:00
|
|
|
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
|
|
|
|
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
|
|
|
|
key->number = 0;
|
|
|
|
|
|
|
|
|
|
switch (key->type) {
|
|
|
|
|
case APFS_TYPE_SNAP_METADATA:
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(struct apfs_snap_metadata_key)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2022-09-26 22:24:32 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2022-09-26 22:24:32 -03:00
|
|
|
key->name = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
case APFS_TYPE_SNAP_NAME:
|
|
|
|
|
if (size < sizeof(struct apfs_snap_name_key) + 1 || *((char *)raw + size - 1) != 0) {
|
|
|
|
|
/* snapshot name must have NULL-termination */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(NULL, "invalid snap name key (%d)", size);
|
2022-09-26 22:24:32 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
key->name = ((struct apfs_snap_name_key *)raw)->name;
|
|
|
|
|
return 0;
|
|
|
|
|
default:
|
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key)
|
|
|
|
|
{
|
|
|
|
|
__le64 *xid = NULL;
|
|
|
|
|
|
2023-03-24 22:07:47 -03:00
|
|
|
if (size != sizeof(*xid)) {
|
|
|
|
|
apfs_err(NULL, "bad key length (%d)", size);
|
2022-09-26 22:24:32 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2022-09-26 22:24:32 -03:00
|
|
|
xid = raw;
|
|
|
|
|
|
|
|
|
|
key->id = le64_to_cpup(xid);
|
|
|
|
|
key->number = 0;
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
key->type = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_init_drec_key - Initialize an in-memory key for a dentry query
|
|
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @ino: inode number of the parent directory
|
|
|
|
|
* @name: filename (NULL for a multiple query)
|
2021-11-09 17:32:37 -03:00
|
|
|
* @name_len: filename length (0 if NULL)
|
2021-03-31 17:16:24 -03:00
|
|
|
* @key: apfs_key structure to initialize
|
|
|
|
|
*/
|
|
|
|
|
void apfs_init_drec_key(struct super_block *sb, u64 ino, const char *name,
|
2022-02-03 16:44:59 -03:00
|
|
|
unsigned int name_len, struct apfs_key *key)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
|
|
|
|
struct apfs_unicursor cursor;
|
|
|
|
|
bool case_fold = apfs_is_case_insensitive(sb);
|
|
|
|
|
u32 hash = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
|
|
key->id = ino;
|
|
|
|
|
key->type = APFS_TYPE_DIR_REC;
|
|
|
|
|
if (!apfs_is_normalization_insensitive(sb)) {
|
|
|
|
|
key->name = name;
|
|
|
|
|
key->number = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* To respect normalization, queries can only consider the hash */
|
|
|
|
|
key->name = NULL;
|
|
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
|
key->number = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 17:32:37 -03:00
|
|
|
apfs_init_unicursor(&cursor, name, name_len);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
unicode_t utf32;
|
|
|
|
|
|
|
|
|
|
utf32 = apfs_normalize_next(&cursor, case_fold);
|
|
|
|
|
if (!utf32)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
hash = crc32c(hash, &utf32, sizeof(utf32));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The filename length doesn't matter, so it's left as zero */
|
|
|
|
|
key->number = hash << APFS_DREC_HASH_SHIFT;
|
|
|
|
|
}
|