cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dir.c (11588B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *
      4 * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
      5 *
      6 *  Directory handling functions for NTFS-based filesystems.
      7 *
      8 */
      9
     10#include <linux/fs.h>
     11#include <linux/nls.h>
     12
     13#include "debug.h"
     14#include "ntfs.h"
     15#include "ntfs_fs.h"
     16
     17/* Convert little endian UTF-16 to NLS string. */
     18int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len,
     19		      u8 *buf, int buf_len)
     20{
     21	int ret, warn;
     22	u8 *op;
     23	struct nls_table *nls = sbi->options->nls;
     24
     25	static_assert(sizeof(wchar_t) == sizeof(__le16));
     26
     27	if (!nls) {
     28		/* UTF-16 -> UTF-8 */
     29		ret = utf16s_to_utf8s(name, len, UTF16_LITTLE_ENDIAN, buf,
     30				      buf_len);
     31		buf[ret] = '\0';
     32		return ret;
     33	}
     34
     35	op = buf;
     36	warn = 0;
     37
     38	while (len--) {
     39		u16 ec;
     40		int charlen;
     41		char dump[5];
     42
     43		if (buf_len < NLS_MAX_CHARSET_SIZE) {
     44			ntfs_warn(sbi->sb,
     45				  "filename was truncated while converting.");
     46			break;
     47		}
     48
     49		ec = le16_to_cpu(*name++);
     50		charlen = nls->uni2char(ec, op, buf_len);
     51
     52		if (charlen > 0) {
     53			op += charlen;
     54			buf_len -= charlen;
     55			continue;
     56		}
     57
     58		*op++ = '_';
     59		buf_len -= 1;
     60		if (warn)
     61			continue;
     62
     63		warn = 1;
     64		hex_byte_pack(&dump[0], ec >> 8);
     65		hex_byte_pack(&dump[2], ec);
     66		dump[4] = 0;
     67
     68		ntfs_err(sbi->sb, "failed to convert \"%s\" to %s", dump,
     69			 nls->charset);
     70	}
     71
     72	*op = '\0';
     73	return op - buf;
     74}
     75
     76// clang-format off
     77#define PLANE_SIZE	0x00010000
     78
     79#define SURROGATE_PAIR	0x0000d800
     80#define SURROGATE_LOW	0x00000400
     81#define SURROGATE_BITS	0x000003ff
     82// clang-format on
     83
     84/*
     85 * put_utf16 - Modified version of put_utf16 from fs/nls/nls_base.c
     86 *
     87 * Function is sparse warnings free.
     88 */
     89static inline void put_utf16(wchar_t *s, unsigned int c,
     90			     enum utf16_endian endian)
     91{
     92	static_assert(sizeof(wchar_t) == sizeof(__le16));
     93	static_assert(sizeof(wchar_t) == sizeof(__be16));
     94
     95	switch (endian) {
     96	default:
     97		*s = (wchar_t)c;
     98		break;
     99	case UTF16_LITTLE_ENDIAN:
    100		*(__le16 *)s = __cpu_to_le16(c);
    101		break;
    102	case UTF16_BIG_ENDIAN:
    103		*(__be16 *)s = __cpu_to_be16(c);
    104		break;
    105	}
    106}
    107
    108/*
    109 * _utf8s_to_utf16s
    110 *
    111 * Modified version of 'utf8s_to_utf16s' allows to
    112 * detect -ENAMETOOLONG without writing out of expected maximum.
    113 */
    114static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian,
    115			    wchar_t *pwcs, int maxout)
    116{
    117	u16 *op;
    118	int size;
    119	unicode_t u;
    120
    121	op = pwcs;
    122	while (inlen > 0 && *s) {
    123		if (*s & 0x80) {
    124			size = utf8_to_utf32(s, inlen, &u);
    125			if (size < 0)
    126				return -EINVAL;
    127			s += size;
    128			inlen -= size;
    129
    130			if (u >= PLANE_SIZE) {
    131				if (maxout < 2)
    132					return -ENAMETOOLONG;
    133
    134				u -= PLANE_SIZE;
    135				put_utf16(op++,
    136					  SURROGATE_PAIR |
    137						  ((u >> 10) & SURROGATE_BITS),
    138					  endian);
    139				put_utf16(op++,
    140					  SURROGATE_PAIR | SURROGATE_LOW |
    141						  (u & SURROGATE_BITS),
    142					  endian);
    143				maxout -= 2;
    144			} else {
    145				if (maxout < 1)
    146					return -ENAMETOOLONG;
    147
    148				put_utf16(op++, u, endian);
    149				maxout--;
    150			}
    151		} else {
    152			if (maxout < 1)
    153				return -ENAMETOOLONG;
    154
    155			put_utf16(op++, *s++, endian);
    156			inlen--;
    157			maxout--;
    158		}
    159	}
    160	return op - pwcs;
    161}
    162
    163/*
    164 * ntfs_nls_to_utf16 - Convert input string to UTF-16.
    165 * @name:	Input name.
    166 * @name_len:	Input name length.
    167 * @uni:	Destination memory.
    168 * @max_ulen:	Destination memory.
    169 * @endian:	Endian of target UTF-16 string.
    170 *
    171 * This function is called:
    172 * - to create NTFS name
    173 * - to create symlink
    174 *
    175 * Return: UTF-16 string length or error (if negative).
    176 */
    177int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
    178		      struct cpu_str *uni, u32 max_ulen,
    179		      enum utf16_endian endian)
    180{
    181	int ret, slen;
    182	const u8 *end;
    183	struct nls_table *nls = sbi->options->nls;
    184	u16 *uname = uni->name;
    185
    186	static_assert(sizeof(wchar_t) == sizeof(u16));
    187
    188	if (!nls) {
    189		/* utf8 -> utf16 */
    190		ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen);
    191		uni->len = ret;
    192		return ret;
    193	}
    194
    195	for (ret = 0, end = name + name_len; name < end; ret++, name += slen) {
    196		if (ret >= max_ulen)
    197			return -ENAMETOOLONG;
    198
    199		slen = nls->char2uni(name, end - name, uname + ret);
    200		if (!slen)
    201			return -EINVAL;
    202		if (slen < 0)
    203			return slen;
    204	}
    205
    206#ifdef __BIG_ENDIAN
    207	if (endian == UTF16_LITTLE_ENDIAN) {
    208		int i = ret;
    209
    210		while (i--) {
    211			__cpu_to_le16s(uname);
    212			uname++;
    213		}
    214	}
    215#else
    216	if (endian == UTF16_BIG_ENDIAN) {
    217		int i = ret;
    218
    219		while (i--) {
    220			__cpu_to_be16s(uname);
    221			uname++;
    222		}
    223	}
    224#endif
    225
    226	uni->len = ret;
    227	return ret;
    228}
    229
    230/*
    231 * dir_search_u - Helper function.
    232 */
    233struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni,
    234			   struct ntfs_fnd *fnd)
    235{
    236	int err = 0;
    237	struct super_block *sb = dir->i_sb;
    238	struct ntfs_sb_info *sbi = sb->s_fs_info;
    239	struct ntfs_inode *ni = ntfs_i(dir);
    240	struct NTFS_DE *e;
    241	int diff;
    242	struct inode *inode = NULL;
    243	struct ntfs_fnd *fnd_a = NULL;
    244
    245	if (!fnd) {
    246		fnd_a = fnd_get();
    247		if (!fnd_a) {
    248			err = -ENOMEM;
    249			goto out;
    250		}
    251		fnd = fnd_a;
    252	}
    253
    254	err = indx_find(&ni->dir, ni, NULL, uni, 0, sbi, &diff, &e, fnd);
    255
    256	if (err)
    257		goto out;
    258
    259	if (diff) {
    260		err = -ENOENT;
    261		goto out;
    262	}
    263
    264	inode = ntfs_iget5(sb, &e->ref, uni);
    265	if (!IS_ERR(inode) && is_bad_inode(inode)) {
    266		iput(inode);
    267		err = -EINVAL;
    268	}
    269out:
    270	fnd_put(fnd_a);
    271
    272	return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
    273}
    274
    275static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
    276			       const struct NTFS_DE *e, u8 *name,
    277			       struct dir_context *ctx)
    278{
    279	const struct ATTR_FILE_NAME *fname;
    280	unsigned long ino;
    281	int name_len;
    282	u32 dt_type;
    283
    284	fname = Add2Ptr(e, sizeof(struct NTFS_DE));
    285
    286	if (fname->type == FILE_NAME_DOS)
    287		return 0;
    288
    289	if (!mi_is_ref(&ni->mi, &fname->home))
    290		return 0;
    291
    292	ino = ino_get(&e->ref);
    293
    294	if (ino == MFT_REC_ROOT)
    295		return 0;
    296
    297	/* Skip meta files. Unless option to show metafiles is set. */
    298	if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino))
    299		return 0;
    300
    301	if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
    302		return 0;
    303
    304	name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name,
    305				     PATH_MAX);
    306	if (name_len <= 0) {
    307		ntfs_warn(sbi->sb, "failed to convert name for inode %lx.",
    308			  ino);
    309		return 0;
    310	}
    311
    312	dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
    313
    314	return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
    315}
    316
    317/*
    318 * ntfs_read_hdr - Helper function for ntfs_readdir().
    319 */
    320static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
    321			 const struct INDEX_HDR *hdr, u64 vbo, u64 pos,
    322			 u8 *name, struct dir_context *ctx)
    323{
    324	int err;
    325	const struct NTFS_DE *e;
    326	u32 e_size;
    327	u32 end = le32_to_cpu(hdr->used);
    328	u32 off = le32_to_cpu(hdr->de_off);
    329
    330	for (;; off += e_size) {
    331		if (off + sizeof(struct NTFS_DE) > end)
    332			return -1;
    333
    334		e = Add2Ptr(hdr, off);
    335		e_size = le16_to_cpu(e->size);
    336		if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
    337			return -1;
    338
    339		if (de_is_last(e))
    340			return 0;
    341
    342		/* Skip already enumerated. */
    343		if (vbo + off < pos)
    344			continue;
    345
    346		if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME)
    347			return -1;
    348
    349		ctx->pos = vbo + off;
    350
    351		/* Submit the name to the filldir callback. */
    352		err = ntfs_filldir(sbi, ni, e, name, ctx);
    353		if (err)
    354			return err;
    355	}
    356}
    357
    358/*
    359 * ntfs_readdir - file_operations::iterate_shared
    360 *
    361 * Use non sorted enumeration.
    362 * We have an example of broken volume where sorted enumeration
    363 * counts each name twice.
    364 */
    365static int ntfs_readdir(struct file *file, struct dir_context *ctx)
    366{
    367	const struct INDEX_ROOT *root;
    368	u64 vbo;
    369	size_t bit;
    370	loff_t eod;
    371	int err = 0;
    372	struct inode *dir = file_inode(file);
    373	struct ntfs_inode *ni = ntfs_i(dir);
    374	struct super_block *sb = dir->i_sb;
    375	struct ntfs_sb_info *sbi = sb->s_fs_info;
    376	loff_t i_size = i_size_read(dir);
    377	u32 pos = ctx->pos;
    378	u8 *name = NULL;
    379	struct indx_node *node = NULL;
    380	u8 index_bits = ni->dir.index_bits;
    381
    382	/* Name is a buffer of PATH_MAX length. */
    383	static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
    384
    385	eod = i_size + sbi->record_size;
    386
    387	if (pos >= eod)
    388		return 0;
    389
    390	if (!dir_emit_dots(file, ctx))
    391		return 0;
    392
    393	/* Allocate PATH_MAX bytes. */
    394	name = __getname();
    395	if (!name)
    396		return -ENOMEM;
    397
    398	if (!ni->mi_loaded && ni->attr_list.size) {
    399		/*
    400		 * Directory inode is locked for read.
    401		 * Load all subrecords to avoid 'write' access to 'ni' during
    402		 * directory reading.
    403		 */
    404		ni_lock(ni);
    405		if (!ni->mi_loaded && ni->attr_list.size) {
    406			err = ni_load_all_mi(ni);
    407			if (!err)
    408				ni->mi_loaded = true;
    409		}
    410		ni_unlock(ni);
    411		if (err)
    412			goto out;
    413	}
    414
    415	root = indx_get_root(&ni->dir, ni, NULL, NULL);
    416	if (!root) {
    417		err = -EINVAL;
    418		goto out;
    419	}
    420
    421	if (pos >= sbi->record_size) {
    422		bit = (pos - sbi->record_size) >> index_bits;
    423	} else {
    424		err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
    425		if (err)
    426			goto out;
    427		bit = 0;
    428	}
    429
    430	if (!i_size) {
    431		ctx->pos = eod;
    432		goto out;
    433	}
    434
    435	for (;;) {
    436		vbo = (u64)bit << index_bits;
    437		if (vbo >= i_size) {
    438			ctx->pos = eod;
    439			goto out;
    440		}
    441
    442		err = indx_used_bit(&ni->dir, ni, &bit);
    443		if (err)
    444			goto out;
    445
    446		if (bit == MINUS_ONE_T) {
    447			ctx->pos = eod;
    448			goto out;
    449		}
    450
    451		vbo = (u64)bit << index_bits;
    452		if (vbo >= i_size) {
    453			ntfs_inode_err(dir, "Looks like your dir is corrupt");
    454			err = -EINVAL;
    455			goto out;
    456		}
    457
    458		err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
    459				&node);
    460		if (err)
    461			goto out;
    462
    463		err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
    464				    vbo + sbi->record_size, pos, name, ctx);
    465		if (err)
    466			goto out;
    467
    468		bit += 1;
    469	}
    470
    471out:
    472
    473	__putname(name);
    474	put_indx_node(node);
    475
    476	if (err == -ENOENT) {
    477		err = 0;
    478		ctx->pos = pos;
    479	}
    480
    481	return err;
    482}
    483
    484static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
    485			  size_t *files)
    486{
    487	int err = 0;
    488	struct ntfs_inode *ni = ntfs_i(dir);
    489	struct NTFS_DE *e = NULL;
    490	struct INDEX_ROOT *root;
    491	struct INDEX_HDR *hdr;
    492	const struct ATTR_FILE_NAME *fname;
    493	u32 e_size, off, end;
    494	u64 vbo = 0;
    495	size_t drs = 0, fles = 0, bit = 0;
    496	loff_t i_size = ni->vfs_inode.i_size;
    497	struct indx_node *node = NULL;
    498	u8 index_bits = ni->dir.index_bits;
    499
    500	if (is_empty)
    501		*is_empty = true;
    502
    503	root = indx_get_root(&ni->dir, ni, NULL, NULL);
    504	if (!root)
    505		return -EINVAL;
    506
    507	hdr = &root->ihdr;
    508
    509	for (;;) {
    510		end = le32_to_cpu(hdr->used);
    511		off = le32_to_cpu(hdr->de_off);
    512
    513		for (; off + sizeof(struct NTFS_DE) <= end; off += e_size) {
    514			e = Add2Ptr(hdr, off);
    515			e_size = le16_to_cpu(e->size);
    516			if (e_size < sizeof(struct NTFS_DE) ||
    517			    off + e_size > end)
    518				break;
    519
    520			if (de_is_last(e))
    521				break;
    522
    523			fname = de_get_fname(e);
    524			if (!fname)
    525				continue;
    526
    527			if (fname->type == FILE_NAME_DOS)
    528				continue;
    529
    530			if (is_empty) {
    531				*is_empty = false;
    532				if (!dirs && !files)
    533					goto out;
    534			}
    535
    536			if (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY)
    537				drs += 1;
    538			else
    539				fles += 1;
    540		}
    541
    542		if (vbo >= i_size)
    543			goto out;
    544
    545		err = indx_used_bit(&ni->dir, ni, &bit);
    546		if (err)
    547			goto out;
    548
    549		if (bit == MINUS_ONE_T)
    550			goto out;
    551
    552		vbo = (u64)bit << index_bits;
    553		if (vbo >= i_size)
    554			goto out;
    555
    556		err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
    557				&node);
    558		if (err)
    559			goto out;
    560
    561		hdr = &node->index->ihdr;
    562		bit += 1;
    563		vbo = (u64)bit << ni->dir.idx2vbn_bits;
    564	}
    565
    566out:
    567	put_indx_node(node);
    568	if (dirs)
    569		*dirs = drs;
    570	if (files)
    571		*files = fles;
    572
    573	return err;
    574}
    575
    576bool dir_is_empty(struct inode *dir)
    577{
    578	bool is_empty = false;
    579
    580	ntfs_dir_count(dir, &is_empty, NULL, NULL);
    581
    582	return is_empty;
    583}
    584
    585// clang-format off
    586const struct file_operations ntfs_dir_operations = {
    587	.llseek		= generic_file_llseek,
    588	.read		= generic_read_dir,
    589	.iterate_shared	= ntfs_readdir,
    590	.fsync		= generic_file_fsync,
    591	.open		= ntfs_file_open,
    592};
    593// clang-format on