// SPDX-License-Identifier: GPL-2.0-or-later
/*
* NTFS kernel directory operations.
*
* Copyright (c) 2001-2007 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
* Copyright (c) 2025 LG Electronics Co., Ltd.
*/
#include <linux/blkdev.h>
#include "dir.h"
#include "mft.h"
#include "ntfs.h"
#include "index.h"
#include "reparse.h"
#include <linux/filelock.h>
/*
* The little endian Unicode string $I30 as a global constant.
*/
__le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'),
cpu_to_le16('3'), cpu_to_le16('0'), 0 };
/*
* ntfs_lookup_inode_by_name - find an inode in a directory given its name
* @dir_ni: ntfs inode of the directory in which to search for the name
* @uname: Unicode name for which to search in the directory
* @uname_len: length of the name @uname in Unicode characters
* @res: return the found file name if necessary (see below)
*
* Look for an inode with name @uname in the directory with inode @dir_ni.
* ntfs_lookup_inode_by_name() walks the contents of the directory looking for
* the Unicode name. If the name is found in the directory, the corresponding
* inode number (>= 0) is returned as a mft reference in cpu format, i.e. it
* is a 64-bit number containing the sequence number.
*
* On error, a negative value is returned corresponding to the error code. In
* particular if the inode is not found -ENOENT is returned. Note that you
* can't just check the return value for being negative, you have to check the
* inode number for being negative which you can extract using MREC(return
* value).
*
* Note, @uname_len does not include the (optional) terminating NULL character.
*
* Note, we look for a case sensitive match first but we also look for a case
* insensitive match at the same time. If we find a case insensitive match, we
* save that for the case that we don't find an exact match, where we return
* the case insensitive match and setup @res (which we allocate!) with the mft
* reference, the file name type, length and with a copy of the little endian
* Unicode file name itself. If we match a file name which is in the DOS name
* space, we only return the mft reference and file name type in @res.
* ntfs_lookup() then uses this to find the long file name in the inode itself.
* This is to avoid polluting the dcache with short file names. We want them to
* work but we don't care for how quickly one can access them. This also fixes
* the dcache aliasing issues.
*
* Locking: - Caller must hold i_mutex on the directory.
* - Each page cache page in the index allocation mapping must be
* locked whilst being accessed otherwise we may find a corrupt
* page due to it being under ->writepage at the moment which
* applies the mst protection fixups before writing out and then
* removes them again after the write is complete after which it
* unlocks the page.
*/
u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
const int uname_len, struct ntfs_name **res)
{
struct ntfs_volume *vol = dir_ni->vol;
struct super_block *sb = vol->sb;
struct inode *ia_vi = NULL;
struct mft_record *m;
struct index_root *ir;
struct index_entry *ie;
struct index_block *ia;
u8 *index_end;
u64 mref;
struct ntfs_attr_search_ctx *ctx;
int err, rc;
s64 vcn, old_vcn;
struct address_space *ia_mapping;
struct folio *folio;
u8 *kaddr = NULL;
struct ntfs_name *name = NULL;
/* Get hold of the mft record for the directory. */
m = map_mft_record(dir_ni);
if (IS_ERR(m)) {
ntfs_error(sb, "map_mft_record() failed with error code %ld.",
-PTR_ERR(m));
return ERR_MREF(PTR_ERR(m));
}
ctx = ntfs_attr_get_search_ctx(dir_ni, m);
if (unlikely(!ctx)) {
err = -ENOMEM;
goto err_out;
}
/* Find the index root attribute in the mft record. */
err = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL,
0, ctx);
if