Commit 5218cd10 authored by Namjae Jeon's avatar Namjae Jeon
Browse files

ntfs: update misc operations



Updates various miscellaneous operations including collation,
debugging, logfile handling, unicode string processing, bdev io helpers,
object id system file handling.

Acked-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent fc053f05
Loading
Loading
Loading
Loading

fs/ntfs/bdev-io.c

0 → 100644
+117 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * NTFS block device I/O.
 *
 * Copyright (c) 2026 LG Electronics Co., Ltd.
 */

#include <linux/blkdev.h>

#include "ntfs.h"

/*
 * ntfs_bdev_read - Read data directly from block device using bio
 * @bdev:	block device to read from
 * @data:	destination buffer
 * @start:	starting byte offset on the block device
 * @size:	number of bytes to read
 *
 * Reads @size bytes starting from byte offset @start directly from the block
 * device using one or more BIOs. This function bypasses the page cache
 * completely and performs synchronous I/O with REQ_META | REQ_SYNC flags set.
 *
 * The @start offset must be sector-aligned (512 bytes). If it is not aligned,
 * the function will return -EINVAL.
 *
 * If the destination buffer @data is not a vmalloc address, it falls back
 * to the more efficient bdev_rw_virt() helper.
 *
 * Return: 0 on success, negative error code on failure.
 */
int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size)
{
	unsigned int done = 0, added;
	int error;
	struct bio *bio;
	enum req_op op;
	sector_t sector = start >> SECTOR_SHIFT;

	if (start & (SECTOR_SIZE - 1))
		return -EINVAL;

	op = REQ_OP_READ | REQ_META | REQ_SYNC;
	if (!is_vmalloc_addr(data))
		return bdev_rw_virt(bdev, sector, data, size, op);

	bio = bio_alloc(bdev,
			bio_max_segs(DIV_ROUND_UP(size, PAGE_SIZE)),
			op, GFP_KERNEL);
	bio->bi_iter.bi_sector = sector;

	do {
		added = bio_add_vmalloc_chunk(bio, data + done, size - done);
		if (!added) {
			struct bio	*prev = bio;

			bio = bio_alloc(prev->bi_bdev,
					bio_max_segs(DIV_ROUND_UP(size - done, PAGE_SIZE)),
					prev->bi_opf, GFP_KERNEL);
			bio->bi_iter.bi_sector = bio_end_sector(prev);
			bio_chain(prev, bio);
			submit_bio(prev);
		}
		done += added;
	} while (done < size);

	error = submit_bio_wait(bio);
	bio_put(bio);

	if (op == REQ_OP_READ)
		invalidate_kernel_vmap_range(data, size);
	return error;
}

/*
 * ntfs_bdev_write - Update block device contents via page cache
 * @sb:		super block of the mounted NTFS filesystem
 * @buf:	source buffer containing data to write
 * @start:	starting byte offset on the block device
 * @size:	number of bytes to write
 *
 * Writes @size bytes from @buf to the block device (sb->s_bdev) starting
 * at byte offset @start. The write is performed entirely through the page
 * cache of the block device's address space.
 */
int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size)
{
	pgoff_t idx, idx_end;
	loff_t offset, end = start + size;
	u32 from, to, buf_off = 0;
	struct folio *folio;

	idx = start >> PAGE_SHIFT;
	idx_end = end >> PAGE_SHIFT;
	from = start & ~PAGE_MASK;

	if (idx == idx_end)
		idx_end++;

	for (; idx < idx_end; idx++, from = 0) {
		folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL);
		if (IS_ERR(folio)) {
			ntfs_error(sb, "Unable to read %ld page", idx);
			return PTR_ERR(folio);
		}

		offset = (loff_t)idx << PAGE_SHIFT;
		to = min_t(u32, end - offset, PAGE_SIZE);

		memcpy_to_folio(folio, from, buf + buf_off, to);
		buf_off += to;
		folio_mark_uptodate(folio);
		folio_mark_dirty(folio);
		folio_put(folio);
	}

	return 0;
}
+94 −58
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * collate.c - NTFS kernel collation handling.  Part of the Linux-NTFS project.
 * NTFS kernel collation handling.
 *
 * Copyright (c) 2004 Anton Altaparmakov
 *
 * Part of this file is based on code from the NTFS-3G.
 * and is copyrighted by the respective authors below:
 * Copyright (c) 2004 Anton Altaparmakov
 * Copyright (c) 2005 Yura Pakhuchiy
 */

#include "collate.h"
#include "debug.h"
#include "ntfs.h"

static int ntfs_collate_binary(ntfs_volume *vol,
		const void *data1, const int data1_len,
		const void *data2, const int data2_len)
#include <linux/sort.h>

static int ntfs_collate_binary(struct ntfs_volume *vol,
		const void *data1, const u32 data1_len,
		const void *data2, const u32 data2_len)
{
	int rc;

	ntfs_debug("Entering.");
	rc = memcmp(data1, data2, min(data1_len, data2_len));
	if (!rc && (data1_len != data2_len)) {
		if (data1_len < data2_len)
@@ -23,23 +29,19 @@ static int ntfs_collate_binary(ntfs_volume *vol,
		else
			rc = 1;
	}
	ntfs_debug("Done, returning %i", rc);
	return rc;
}

static int ntfs_collate_ntofs_ulong(ntfs_volume *vol,
		const void *data1, const int data1_len,
		const void *data2, const int data2_len)
static int ntfs_collate_ntofs_ulong(struct ntfs_volume *vol,
		const void *data1, const u32 data1_len,
		const void *data2, const u32 data2_len)
{
	int rc;
	u32 d1, d2;
	u32 d1 = le32_to_cpup(data1), d2 = le32_to_cpup(data2);

	if (data1_len != data2_len || data1_len != 4)
		return -EINVAL;

	ntfs_debug("Entering.");
	// FIXME:  We don't really want to bug here.
	BUG_ON(data1_len != data2_len);
	BUG_ON(data1_len != 4);
	d1 = le32_to_cpup(data1);
	d2 = le32_to_cpup(data2);
	if (d1 < d2)
		rc = -1;
	else {
@@ -48,27 +50,65 @@ static int ntfs_collate_ntofs_ulong(ntfs_volume *vol,
		else
			rc = 1;
	}
	ntfs_debug("Done, returning %i", rc);
	return rc;
}

typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int,
		const void *, const int);
/*
 * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first
 * @vol: ntfs volume
 * @data1: first ulong array to collate
 * @data1_len: length in bytes of @data1
 * @data2: second ulong array to collate
 * @data2_len: length in bytes of @data2
 *
 * Returns: -1, 0 or 1 depending of how the arrays compare
 */
static int ntfs_collate_ntofs_ulongs(struct ntfs_volume *vol,
		const void *data1, const u32 data1_len,
		const void *data2, const u32 data2_len)
{
	int len;
	const __le32 *p1 = data1, *p2 = data2;
	u32 d1, d2;

static ntfs_collate_func_t ntfs_do_collate0x0[3] = {
	ntfs_collate_binary,
	NULL/*ntfs_collate_file_name*/,
	NULL/*ntfs_collate_unicode_string*/,
};
	if (data1_len != data2_len || data1_len & 3) {
		ntfs_error(vol->sb, "data1_len or data2_len not valid\n");
		return -1;
	}

static ntfs_collate_func_t ntfs_do_collate0x1[4] = {
	ntfs_collate_ntofs_ulong,
	NULL/*ntfs_collate_ntofs_sid*/,
	NULL/*ntfs_collate_ntofs_security_hash*/,
	NULL/*ntfs_collate_ntofs_ulongs*/,
};
	len = data1_len;
	do {
		d1 = le32_to_cpup(p1);
		p1++;
		d2 = le32_to_cpup(p2);
		p2++;
	} while (d1 == d2 && (len -= 4) > 0);
	return cmp_int(d1, d2);
}

/**
/*
 * ntfs_collate_file_name - Which of two filenames should be listed first
 * @vol: ntfs volume
 * @data1: first filename to collate
 * @data1_len: length in bytes of @data1(unused)
 * @data2: second filename to collate
 * @data2_len: length in bytes of @data2(unused)
 */
static int ntfs_collate_file_name(struct ntfs_volume *vol,
		const void *data1, const u32 data1_len,
		const void *data2, const u32 data2_len)
{
	int rc;

	rc = ntfs_file_compare_values(data1, data2, -EINVAL,
			IGNORE_CASE, vol->upcase, vol->upcase_len);
	if (!rc)
		rc = ntfs_file_compare_values(data1, data2,
			-EINVAL, CASE_SENSITIVE, vol->upcase, vol->upcase_len);
	return rc;
}

/*
 * ntfs_collate - collate two data items using a specified collation rule
 * @vol:	ntfs volume to which the data items belong
 * @cr:		collation rule to use when comparing the items
@@ -79,32 +119,28 @@ static ntfs_collate_func_t ntfs_do_collate0x1[4] = {
 *
 * Collate the two data items @data1 and @data2 using the collation rule @cr
 * and return -1, 0, ir 1 if @data1 is found, respectively, to collate before,
 * to match, or to collate after @data2.
 *
 * For speed we use the collation rule @cr as an index into two tables of
 * function pointers to call the appropriate collation function.
 * to match, or to collate after @data2. return -EINVAL if an error occurred.
 */
int ntfs_collate(ntfs_volume *vol, COLLATION_RULE cr,
		const void *data1, const int data1_len,
		const void *data2, const int data2_len) {
	int i;

	ntfs_debug("Entering.");
	/*
	 * FIXME:  At the moment we only support COLLATION_BINARY and
	 * COLLATION_NTOFS_ULONG, so we BUG() for everything else for now.
	 */
	BUG_ON(cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG);
	i = le32_to_cpu(cr);
	BUG_ON(i < 0);
	if (i <= 0x02)
		return ntfs_do_collate0x0[i](vol, data1, data1_len,
int ntfs_collate(struct ntfs_volume *vol, __le32 cr,
		const void *data1, const u32 data1_len,
		const void *data2, const u32 data2_len)
{
	switch (le32_to_cpu(cr)) {
	case le32_to_cpu(COLLATION_BINARY):
		return ntfs_collate_binary(vol, data1, data1_len,
					   data2, data2_len);
	BUG_ON(i < 0x10);
	i -= 0x10;
	if (likely(i <= 3))
		return ntfs_do_collate0x1[i](vol, data1, data1_len,
	case le32_to_cpu(COLLATION_FILE_NAME):
		return ntfs_collate_file_name(vol, data1, data1_len,
					      data2, data2_len);
	BUG();
	return 0;
	case le32_to_cpu(COLLATION_NTOFS_ULONG):
		return ntfs_collate_ntofs_ulong(vol, data1, data1_len,
						data2, data2_len);
	case le32_to_cpu(COLLATION_NTOFS_ULONGS):
		return ntfs_collate_ntofs_ulongs(vol, data1, data1_len,
						 data2, data2_len);
	default:
		ntfs_error(vol->sb, "Unknown collation rule 0x%x",
			   le32_to_cpu(cr));
		return -EINVAL;
	}
}
+30 −18
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * debug.c - NTFS kernel debug support. Part of the Linux-NTFS project.
 * NTFS kernel debug support.
 *
 * Copyright (c) 2001-2004 Anton Altaparmakov
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "debug.h"

/**
/*
 * __ntfs_warning - output a warning to the syslog
 * @function:	name of function outputting the warning
 * @sb:		super block of mounted ntfs filesystem
@@ -33,24 +33,28 @@ void __ntfs_warning(const char *function, const struct super_block *sb,
	va_list args;
	int flen = 0;

#ifndef DEBUG
	if (!printk_ratelimit())
		return;
#endif
	if (function)
		flen = strlen(function);
	va_start(args, fmt);
	vaf.fmt = fmt;
	vaf.va = &args;
#ifdef DEBUG
	if (sb)
		pr_warn("(device %s): %s(): %pV\n",
			sb->s_id, flen ? function : "", &vaf);
	else
		pr_warn("%s(): %pV\n", flen ? function : "", &vaf);
#else
	if (sb)
		pr_warn_ratelimited("(device %s): %s(): %pV\n",
			sb->s_id, flen ? function : "", &vaf);
	else
		pr_warn_ratelimited("%s(): %pV\n", flen ? function : "", &vaf);
#endif
	va_end(args);
}

/**
/*
 * __ntfs_error - output an error to the syslog
 * @function:	name of function outputting the error
 * @sb:		super block of mounted ntfs filesystem
@@ -69,34 +73,41 @@ void __ntfs_warning(const char *function, const struct super_block *sb,
 * Note, you should be using debug.h::ntfs_error(@sb, @fmt, @...) instead
 * as this provides the @function parameter automatically.
 */
void __ntfs_error(const char *function, const struct super_block *sb,
void __ntfs_error(const char *function, struct super_block *sb,
		const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;
	int flen = 0;

#ifndef DEBUG
	if (!printk_ratelimit())
		return;
#endif
	if (function)
		flen = strlen(function);
	va_start(args, fmt);
	vaf.fmt = fmt;
	vaf.va = &args;
#ifdef DEBUG
	if (sb)
		pr_err("(device %s): %s(): %pV\n",
		       sb->s_id, flen ? function : "", &vaf);
	else
		pr_err("%s(): %pV\n", flen ? function : "", &vaf);
#else
	if (sb)
		pr_err_ratelimited("(device %s): %s(): %pV\n",
		       sb->s_id, flen ? function : "", &vaf);
	else
		pr_err_ratelimited("%s(): %pV\n", flen ? function : "", &vaf);
#endif
	va_end(args);

	if (sb)
		ntfs_handle_error(sb);
}

#ifdef DEBUG

/* If 1, output debug messages, and if 0, don't. */
int debug_msgs = 0;
int debug_msgs;

void __ntfs_debug(const char *file, int line, const char *function,
		const char *fmt, ...)
@@ -117,11 +128,12 @@ void __ntfs_debug(const char *file, int line, const char *function,
}

/* Dump a runlist. Caller has to provide synchronisation for @rl. */
void ntfs_debug_dump_runlist(const runlist_element *rl)
void ntfs_debug_dump_runlist(const struct runlist_element *rl)
{
	int i;
	const char *lcn_str[5] = { "LCN_HOLE         ", "LCN_RL_NOT_MAPPED",
				   "LCN_ENOENT       ", "LCN_unknown      " };
	const char *lcn_str[5] = { "LCN_DELALLOC     ", "LCN_HOLE         ",
				   "LCN_RL_NOT_MAPPED", "LCN_ENOENT       ",
				   "LCN_unknown      " };

	if (!debug_msgs)
		return;
@@ -132,9 +144,9 @@ void ntfs_debug_dump_runlist(const runlist_element *rl)
	}
	pr_debug("VCN              LCN               Run length\n");
	for (i = 0; ; i++) {
		LCN lcn = (rl + i)->lcn;
		s64 lcn = (rl + i)->lcn;

		if (lcn < (LCN)0) {
		if (lcn < 0) {
			int index = -lcn - 1;

			if (index > -LCN_ENOENT - 1)
+180 −251

File changed.

Preview size limit exceeded, changes collapsed.

fs/ntfs/object_id.c

0 → 100644
+158 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Pocessing of object ids
 *
 * Part of this file is based on code from the NTFS-3G.
 *
 * Copyright (c) 2009-2019 Jean-Pierre Andre
 * Copyright (c) 2026 LG Electronics Co., Ltd.
 */

#include "ntfs.h"
#include "index.h"
#include "object_id.h"

struct object_id_index_key {
	union {
		u32 alignment;
		struct guid guid;
	} object_id;
} __packed;

struct object_id_index_data {
	__le64 file_id;
	struct guid birth_volume_id;
	struct guid birth_object_id;
	struct guid domain_id;
} __packed;

/* Index entry in $Extend/$ObjId */
struct object_id_index {
	struct index_entry_header header;
	struct object_id_index_key key;
	struct object_id_index_data data;
} __packed;

__le16 objid_index_name[] = {cpu_to_le16('$'), cpu_to_le16('O'), 0};

/*
 * open_object_id_index - Open the $Extend/$ObjId file and its index
 * @vol: NTFS volume structure
 *
 * Opens the $ObjId system file and retrieves its index context.
 *
 * Return: The index context if opened successfully, or NULL if an error
 *	   occurred.
 */
static struct ntfs_index_context *open_object_id_index(struct ntfs_volume *vol)
{
	struct inode *dir_vi, *vi;
	struct ntfs_inode *dir_ni;
	struct ntfs_index_context *xo = NULL;
	struct ntfs_name *name = NULL;
	u64 mref;
	int uname_len;
	__le16 *uname;

	uname_len = ntfs_nlstoucs(vol, "$ObjId", 6, &uname,
			NTFS_MAX_NAME_LEN);
	if (uname_len < 0)
		return NULL;

	/* do not use path_name_to inode - could reopen root */
	dir_vi = ntfs_iget(vol->sb, FILE_Extend);
	if (IS_ERR(dir_vi)) {
		kmem_cache_free(ntfs_name_cache, uname);
		return NULL;
	}
	dir_ni = NTFS_I(dir_vi);

	mutex_lock_nested(&dir_ni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
	mref = ntfs_lookup_inode_by_name(dir_ni, uname, uname_len, &name);
	mutex_unlock(&dir_ni->mrec_lock);
	kfree(name);
	kmem_cache_free(ntfs_name_cache, uname);
	if (IS_ERR_MREF(mref))
		goto put_dir_vi;

	vi = ntfs_iget(vol->sb, MREF(mref));
	if (IS_ERR(vi))
		goto put_dir_vi;

	xo = ntfs_index_ctx_get(NTFS_I(vi), objid_index_name, 2);
	if (!xo)
		iput(vi);
put_dir_vi:
	iput(dir_vi);
	return xo;
}


/*
 * remove_object_id_index - Remove an object id index entry if attribute present
 * @ni: NTFS inode structure containing the attribute
 * @xo:	Index context for the object id index
 *
 * Reads the existing object ID attribute and removes it from the index.
 *
 * Return: 0 on success, or a negative error code on failure.
 */
static int remove_object_id_index(struct ntfs_inode *ni, struct ntfs_index_context *xo)
{
	struct object_id_index_key key = {0};
	s64 size;

	if (ni->data_size == 0)
		return -ENODATA;

	/* read the existing object id attribute */
	size = ntfs_inode_attr_pread(VFS_I(ni), 0, sizeof(struct guid),
				     (char *)&key);
	if (size != sizeof(struct guid))
		return -ENODATA;

	if (!ntfs_index_lookup(&key, sizeof(struct object_id_index_key), xo))
		return ntfs_index_rm(xo);

	return 0;
}

/*
 * ntfs_delete_object_id_index - Delete an object_id index entry
 * @ni:	NTFS inode structure
 *
 * Opens the object ID index and removes the entry corresponding to the inode.
 *
 * Return: 0 on success, or a negative error code on failure.
 */
int ntfs_delete_object_id_index(struct ntfs_inode *ni)
{
	struct ntfs_index_context *xo;
	struct ntfs_inode *xoni;
	struct inode *attr_vi;
	int ret = 0;

	attr_vi = ntfs_attr_iget(VFS_I(ni), AT_OBJECT_ID, AT_UNNAMED, 0);
	if (IS_ERR(attr_vi))
		return PTR_ERR(attr_vi);

	/*
	 * read the existing object id and un-index it
	 */
	xo = open_object_id_index(ni->vol);
	if (xo) {
		xoni = xo->idx_ni;
		mutex_lock_nested(&xoni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
		ret = remove_object_id_index(NTFS_I(attr_vi), xo);
		if (!ret) {
			ntfs_index_entry_mark_dirty(xo);
			mark_mft_record_dirty(xoni);
		}
		ntfs_index_ctx_put(xo);
		mutex_unlock(&xoni->mrec_lock);
		iput(VFS_I(xoni));
	}

	iput(attr_vi);
	return ret;
}
Loading