162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/fat/dir.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  directory handling functions for fat-based filesystems
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Written 1992,1993 by Werner Almesberger
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
1262306a36Sopenharmony_ci *  Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
1362306a36Sopenharmony_ci *  Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
1462306a36Sopenharmony_ci *  Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/compat.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci#include <linux/iversion.h>
2162306a36Sopenharmony_ci#include "fat.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Maximum buffer size of short name.
2562306a36Sopenharmony_ci * [(MSDOS_NAME + '.') * max one char + nul]
2662306a36Sopenharmony_ci * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul]
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci#define FAT_MAX_SHORT_SIZE	((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1)
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Maximum buffer size of unicode chars from slots.
3162306a36Sopenharmony_ci * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)]
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#define FAT_MAX_UNI_CHARS	((MSDOS_SLOTS - 1) * 13 + 1)
3462306a36Sopenharmony_ci#define FAT_MAX_UNI_SIZE	(FAT_MAX_UNI_CHARS * sizeof(wchar_t))
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic inline unsigned char fat_tolower(unsigned char c)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return ((c >= 'A') && (c <= 'Z')) ? c+32 : c;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic inline loff_t fat_make_i_pos(struct super_block *sb,
4262306a36Sopenharmony_ci				    struct buffer_head *bh,
4362306a36Sopenharmony_ci				    struct msdos_dir_entry *de)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits)
4662306a36Sopenharmony_ci		| (de - (struct msdos_dir_entry *)bh->b_data);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic inline void fat_dir_readahead(struct inode *dir, sector_t iblock,
5062306a36Sopenharmony_ci				     sector_t phys)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
5362306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
5462306a36Sopenharmony_ci	struct buffer_head *bh;
5562306a36Sopenharmony_ci	int sec;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* This is not a first sector of cluster, or sec_per_clus == 1 */
5862306a36Sopenharmony_ci	if ((iblock & (sbi->sec_per_clus - 1)) || sbi->sec_per_clus == 1)
5962306a36Sopenharmony_ci		return;
6062306a36Sopenharmony_ci	/* root dir of FAT12/FAT16 */
6162306a36Sopenharmony_ci	if (!is_fat32(sbi) && (dir->i_ino == MSDOS_ROOT_INO))
6262306a36Sopenharmony_ci		return;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	bh = sb_find_get_block(sb, phys);
6562306a36Sopenharmony_ci	if (bh == NULL || !buffer_uptodate(bh)) {
6662306a36Sopenharmony_ci		for (sec = 0; sec < sbi->sec_per_clus; sec++)
6762306a36Sopenharmony_ci			sb_breadahead(sb, phys + sec);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	brelse(bh);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Returns the inode number of the directory entry at offset pos. If bh is
7362306a36Sopenharmony_ci   non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
7462306a36Sopenharmony_ci   returned in bh.
7562306a36Sopenharmony_ci   AV. Most often we do it item-by-item. Makes sense to optimize.
7662306a36Sopenharmony_ci   AV. OK, there we go: if both bh and de are non-NULL we assume that we just
7762306a36Sopenharmony_ci   AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
7862306a36Sopenharmony_ci   AV. It's done in fat_get_entry() (inlined), here the slow case lives.
7962306a36Sopenharmony_ci   AV. Additionally, when we return -1 (i.e. reached the end of directory)
8062306a36Sopenharmony_ci   AV. we make bh NULL.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic int fat__get_entry(struct inode *dir, loff_t *pos,
8362306a36Sopenharmony_ci			  struct buffer_head **bh, struct msdos_dir_entry **de)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
8662306a36Sopenharmony_ci	sector_t phys, iblock;
8762306a36Sopenharmony_ci	unsigned long mapped_blocks;
8862306a36Sopenharmony_ci	int err, offset;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cinext:
9162306a36Sopenharmony_ci	brelse(*bh);
9262306a36Sopenharmony_ci	*bh = NULL;
9362306a36Sopenharmony_ci	iblock = *pos >> sb->s_blocksize_bits;
9462306a36Sopenharmony_ci	err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0, false);
9562306a36Sopenharmony_ci	if (err || !phys)
9662306a36Sopenharmony_ci		return -1;	/* beyond EOF or error */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	fat_dir_readahead(dir, iblock, phys);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	*bh = sb_bread(sb, phys);
10162306a36Sopenharmony_ci	if (*bh == NULL) {
10262306a36Sopenharmony_ci		fat_msg_ratelimit(sb, KERN_ERR,
10362306a36Sopenharmony_ci			"Directory bread(block %llu) failed", (llu)phys);
10462306a36Sopenharmony_ci		/* skip this block */
10562306a36Sopenharmony_ci		*pos = (iblock + 1) << sb->s_blocksize_bits;
10662306a36Sopenharmony_ci		goto next;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	offset = *pos & (sb->s_blocksize - 1);
11062306a36Sopenharmony_ci	*pos += sizeof(struct msdos_dir_entry);
11162306a36Sopenharmony_ci	*de = (struct msdos_dir_entry *)((*bh)->b_data + offset);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic inline int fat_get_entry(struct inode *dir, loff_t *pos,
11762306a36Sopenharmony_ci				struct buffer_head **bh,
11862306a36Sopenharmony_ci				struct msdos_dir_entry **de)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	/* Fast stuff first */
12162306a36Sopenharmony_ci	if (*bh && *de &&
12262306a36Sopenharmony_ci	   (*de - (struct msdos_dir_entry *)(*bh)->b_data) <
12362306a36Sopenharmony_ci				MSDOS_SB(dir->i_sb)->dir_per_block - 1) {
12462306a36Sopenharmony_ci		*pos += sizeof(struct msdos_dir_entry);
12562306a36Sopenharmony_ci		(*de)++;
12662306a36Sopenharmony_ci		return 0;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	return fat__get_entry(dir, pos, bh, de);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII.
13362306a36Sopenharmony_ci * If uni_xlate is enabled and we can't get a 1:1 conversion, use a
13462306a36Sopenharmony_ci * colon as an escape character since it is normally invalid on the vfat
13562306a36Sopenharmony_ci * filesystem. The following four characters are the hexadecimal digits
13662306a36Sopenharmony_ci * of Unicode value. This lets us do a full dump and restore of Unicode
13762306a36Sopenharmony_ci * filenames. We could get into some trouble with long Unicode names,
13862306a36Sopenharmony_ci * but ignore that right now.
13962306a36Sopenharmony_ci * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic int uni16_to_x8(struct super_block *sb, unsigned char *ascii,
14262306a36Sopenharmony_ci		       const wchar_t *uni, int len, struct nls_table *nls)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
14562306a36Sopenharmony_ci	const wchar_t *ip;
14662306a36Sopenharmony_ci	wchar_t ec;
14762306a36Sopenharmony_ci	unsigned char *op;
14862306a36Sopenharmony_ci	int charlen;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ip = uni;
15162306a36Sopenharmony_ci	op = ascii;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	while (*ip && ((len - NLS_MAX_CHARSET_SIZE) > 0)) {
15462306a36Sopenharmony_ci		ec = *ip++;
15562306a36Sopenharmony_ci		charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE);
15662306a36Sopenharmony_ci		if (charlen > 0) {
15762306a36Sopenharmony_ci			op += charlen;
15862306a36Sopenharmony_ci			len -= charlen;
15962306a36Sopenharmony_ci		} else {
16062306a36Sopenharmony_ci			if (uni_xlate == 1) {
16162306a36Sopenharmony_ci				*op++ = ':';
16262306a36Sopenharmony_ci				op = hex_byte_pack(op, ec >> 8);
16362306a36Sopenharmony_ci				op = hex_byte_pack(op, ec);
16462306a36Sopenharmony_ci				len -= 5;
16562306a36Sopenharmony_ci			} else {
16662306a36Sopenharmony_ci				*op++ = '?';
16762306a36Sopenharmony_ci				len--;
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (unlikely(*ip)) {
17362306a36Sopenharmony_ci		fat_msg(sb, KERN_WARNING,
17462306a36Sopenharmony_ci			"filename was truncated while converting.");
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	*op = 0;
17862306a36Sopenharmony_ci	return op - ascii;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic inline int fat_uni_to_x8(struct super_block *sb, const wchar_t *uni,
18262306a36Sopenharmony_ci				unsigned char *buf, int size)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
18562306a36Sopenharmony_ci	if (sbi->options.utf8)
18662306a36Sopenharmony_ci		return utf16s_to_utf8s(uni, FAT_MAX_UNI_CHARS,
18762306a36Sopenharmony_ci				UTF16_HOST_ENDIAN, buf, size);
18862306a36Sopenharmony_ci	else
18962306a36Sopenharmony_ci		return uni16_to_x8(sb, buf, uni, size, sbi->nls_io);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic inline int
19362306a36Sopenharmony_cifat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int charlen;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	charlen = t->char2uni(c, clen, uni);
19862306a36Sopenharmony_ci	if (charlen < 0) {
19962306a36Sopenharmony_ci		*uni = 0x003f;	/* a question mark */
20062306a36Sopenharmony_ci		charlen = 1;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return charlen;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic inline int
20662306a36Sopenharmony_cifat_short2lower_uni(struct nls_table *t, unsigned char *c,
20762306a36Sopenharmony_ci		    int clen, wchar_t *uni)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int charlen;
21062306a36Sopenharmony_ci	wchar_t wc;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	charlen = t->char2uni(c, clen, &wc);
21362306a36Sopenharmony_ci	if (charlen < 0) {
21462306a36Sopenharmony_ci		*uni = 0x003f;	/* a question mark */
21562306a36Sopenharmony_ci		charlen = 1;
21662306a36Sopenharmony_ci	} else if (charlen <= 1) {
21762306a36Sopenharmony_ci		unsigned char nc = t->charset2lower[*c];
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (!nc)
22062306a36Sopenharmony_ci			nc = *c;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		charlen = t->char2uni(&nc, 1, uni);
22362306a36Sopenharmony_ci		if (charlen < 0) {
22462306a36Sopenharmony_ci			*uni = 0x003f;	/* a question mark */
22562306a36Sopenharmony_ci			charlen = 1;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci	} else
22862306a36Sopenharmony_ci		*uni = wc;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return charlen;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic inline int
23462306a36Sopenharmony_cifat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size,
23562306a36Sopenharmony_ci		  wchar_t *uni_buf, unsigned short opt, int lower)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	int len = 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (opt & VFAT_SFN_DISPLAY_LOWER)
24062306a36Sopenharmony_ci		len =  fat_short2lower_uni(nls, buf, buf_size, uni_buf);
24162306a36Sopenharmony_ci	else if (opt & VFAT_SFN_DISPLAY_WIN95)
24262306a36Sopenharmony_ci		len = fat_short2uni(nls, buf, buf_size, uni_buf);
24362306a36Sopenharmony_ci	else if (opt & VFAT_SFN_DISPLAY_WINNT) {
24462306a36Sopenharmony_ci		if (lower)
24562306a36Sopenharmony_ci			len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
24662306a36Sopenharmony_ci		else
24762306a36Sopenharmony_ci			len = fat_short2uni(nls, buf, buf_size, uni_buf);
24862306a36Sopenharmony_ci	} else
24962306a36Sopenharmony_ci		len = fat_short2uni(nls, buf, buf_size, uni_buf);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return len;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic inline int fat_name_match(struct msdos_sb_info *sbi,
25562306a36Sopenharmony_ci				 const unsigned char *a, int a_len,
25662306a36Sopenharmony_ci				 const unsigned char *b, int b_len)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	if (a_len != b_len)
25962306a36Sopenharmony_ci		return 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (sbi->options.name_check != 's')
26262306a36Sopenharmony_ci		return !nls_strnicmp(sbi->nls_io, a, b, a_len);
26362306a36Sopenharmony_ci	else
26462306a36Sopenharmony_ci		return !memcmp(a, b, a_len);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cienum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, };
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/**
27062306a36Sopenharmony_ci * fat_parse_long - Parse extended directory entry.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * This function returns zero on success, negative value on error, or one of
27362306a36Sopenharmony_ci * the following:
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * %PARSE_INVALID - Directory entry is invalid.
27662306a36Sopenharmony_ci * %PARSE_NOT_LONGNAME - Directory entry does not contain longname.
27762306a36Sopenharmony_ci * %PARSE_EOF - Directory has no more entries.
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistatic int fat_parse_long(struct inode *dir, loff_t *pos,
28062306a36Sopenharmony_ci			  struct buffer_head **bh, struct msdos_dir_entry **de,
28162306a36Sopenharmony_ci			  wchar_t **unicode, unsigned char *nr_slots)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct msdos_dir_slot *ds;
28462306a36Sopenharmony_ci	unsigned char id, slot, slots, alias_checksum;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!*unicode) {
28762306a36Sopenharmony_ci		*unicode = __getname();
28862306a36Sopenharmony_ci		if (!*unicode) {
28962306a36Sopenharmony_ci			brelse(*bh);
29062306a36Sopenharmony_ci			return -ENOMEM;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ciparse_long:
29462306a36Sopenharmony_ci	ds = (struct msdos_dir_slot *)*de;
29562306a36Sopenharmony_ci	id = ds->id;
29662306a36Sopenharmony_ci	if (!(id & 0x40))
29762306a36Sopenharmony_ci		return PARSE_INVALID;
29862306a36Sopenharmony_ci	slots = id & ~0x40;
29962306a36Sopenharmony_ci	if (slots > 20 || !slots)	/* ceil(256 * 2 / 26) */
30062306a36Sopenharmony_ci		return PARSE_INVALID;
30162306a36Sopenharmony_ci	*nr_slots = slots;
30262306a36Sopenharmony_ci	alias_checksum = ds->alias_checksum;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	slot = slots;
30562306a36Sopenharmony_ci	while (1) {
30662306a36Sopenharmony_ci		int offset;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		slot--;
30962306a36Sopenharmony_ci		offset = slot * 13;
31062306a36Sopenharmony_ci		fat16_towchar(*unicode + offset, ds->name0_4, 5);
31162306a36Sopenharmony_ci		fat16_towchar(*unicode + offset + 5, ds->name5_10, 6);
31262306a36Sopenharmony_ci		fat16_towchar(*unicode + offset + 11, ds->name11_12, 2);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (ds->id & 0x40)
31562306a36Sopenharmony_ci			(*unicode)[offset + 13] = 0;
31662306a36Sopenharmony_ci		if (fat_get_entry(dir, pos, bh, de) < 0)
31762306a36Sopenharmony_ci			return PARSE_EOF;
31862306a36Sopenharmony_ci		if (slot == 0)
31962306a36Sopenharmony_ci			break;
32062306a36Sopenharmony_ci		ds = (struct msdos_dir_slot *)*de;
32162306a36Sopenharmony_ci		if (ds->attr != ATTR_EXT)
32262306a36Sopenharmony_ci			return PARSE_NOT_LONGNAME;
32362306a36Sopenharmony_ci		if ((ds->id & ~0x40) != slot)
32462306a36Sopenharmony_ci			goto parse_long;
32562306a36Sopenharmony_ci		if (ds->alias_checksum != alias_checksum)
32662306a36Sopenharmony_ci			goto parse_long;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	if ((*de)->name[0] == DELETED_FLAG)
32962306a36Sopenharmony_ci		return PARSE_INVALID;
33062306a36Sopenharmony_ci	if ((*de)->attr == ATTR_EXT)
33162306a36Sopenharmony_ci		goto parse_long;
33262306a36Sopenharmony_ci	if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME))
33362306a36Sopenharmony_ci		return PARSE_INVALID;
33462306a36Sopenharmony_ci	if (fat_checksum((*de)->name) != alias_checksum)
33562306a36Sopenharmony_ci		*nr_slots = 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/**
34162306a36Sopenharmony_ci * fat_parse_short - Parse MS-DOS (short) directory entry.
34262306a36Sopenharmony_ci * @sb:		superblock
34362306a36Sopenharmony_ci * @de:		directory entry to parse
34462306a36Sopenharmony_ci * @name:	FAT_MAX_SHORT_SIZE array in which to place extracted name
34562306a36Sopenharmony_ci * @dot_hidden:	Nonzero == prepend '.' to names with ATTR_HIDDEN
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * Returns the number of characters extracted into 'name'.
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_cistatic int fat_parse_short(struct super_block *sb,
35062306a36Sopenharmony_ci			   const struct msdos_dir_entry *de,
35162306a36Sopenharmony_ci			   unsigned char *name, int dot_hidden)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	const struct msdos_sb_info *sbi = MSDOS_SB(sb);
35462306a36Sopenharmony_ci	int isvfat = sbi->options.isvfat;
35562306a36Sopenharmony_ci	int nocase = sbi->options.nocase;
35662306a36Sopenharmony_ci	unsigned short opt_shortname = sbi->options.shortname;
35762306a36Sopenharmony_ci	struct nls_table *nls_disk = sbi->nls_disk;
35862306a36Sopenharmony_ci	wchar_t uni_name[14];
35962306a36Sopenharmony_ci	unsigned char c, work[MSDOS_NAME];
36062306a36Sopenharmony_ci	unsigned char *ptname = name;
36162306a36Sopenharmony_ci	int chi, chl, i, j, k;
36262306a36Sopenharmony_ci	int dotoffset = 0;
36362306a36Sopenharmony_ci	int name_len = 0, uni_len = 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!isvfat && dot_hidden && (de->attr & ATTR_HIDDEN)) {
36662306a36Sopenharmony_ci		*ptname++ = '.';
36762306a36Sopenharmony_ci		dotoffset = 1;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	memcpy(work, de->name, sizeof(work));
37162306a36Sopenharmony_ci	/* For an explanation of the special treatment of 0x05 in
37262306a36Sopenharmony_ci	 * filenames, see msdos_format_name in namei_msdos.c
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci	if (work[0] == 0x05)
37562306a36Sopenharmony_ci		work[0] = 0xE5;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Filename */
37862306a36Sopenharmony_ci	for (i = 0, j = 0; i < 8;) {
37962306a36Sopenharmony_ci		c = work[i];
38062306a36Sopenharmony_ci		if (!c)
38162306a36Sopenharmony_ci			break;
38262306a36Sopenharmony_ci		chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
38362306a36Sopenharmony_ci					&uni_name[j++], opt_shortname,
38462306a36Sopenharmony_ci					de->lcase & CASE_LOWER_BASE);
38562306a36Sopenharmony_ci		if (chl <= 1) {
38662306a36Sopenharmony_ci			if (!isvfat)
38762306a36Sopenharmony_ci				ptname[i] = nocase ? c : fat_tolower(c);
38862306a36Sopenharmony_ci			i++;
38962306a36Sopenharmony_ci			if (c != ' ') {
39062306a36Sopenharmony_ci				name_len = i;
39162306a36Sopenharmony_ci				uni_len  = j;
39262306a36Sopenharmony_ci			}
39362306a36Sopenharmony_ci		} else {
39462306a36Sopenharmony_ci			uni_len = j;
39562306a36Sopenharmony_ci			if (isvfat)
39662306a36Sopenharmony_ci				i += min(chl, 8-i);
39762306a36Sopenharmony_ci			else {
39862306a36Sopenharmony_ci				for (chi = 0; chi < chl && i < 8; chi++, i++)
39962306a36Sopenharmony_ci					ptname[i] = work[i];
40062306a36Sopenharmony_ci			}
40162306a36Sopenharmony_ci			if (chl)
40262306a36Sopenharmony_ci				name_len = i;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	i = name_len;
40762306a36Sopenharmony_ci	j = uni_len;
40862306a36Sopenharmony_ci	fat_short2uni(nls_disk, ".", 1, &uni_name[j++]);
40962306a36Sopenharmony_ci	if (!isvfat)
41062306a36Sopenharmony_ci		ptname[i] = '.';
41162306a36Sopenharmony_ci	i++;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Extension */
41462306a36Sopenharmony_ci	for (k = 8; k < MSDOS_NAME;) {
41562306a36Sopenharmony_ci		c = work[k];
41662306a36Sopenharmony_ci		if (!c)
41762306a36Sopenharmony_ci			break;
41862306a36Sopenharmony_ci		chl = fat_shortname2uni(nls_disk, &work[k], MSDOS_NAME - k,
41962306a36Sopenharmony_ci					&uni_name[j++], opt_shortname,
42062306a36Sopenharmony_ci					de->lcase & CASE_LOWER_EXT);
42162306a36Sopenharmony_ci		if (chl <= 1) {
42262306a36Sopenharmony_ci			k++;
42362306a36Sopenharmony_ci			if (!isvfat)
42462306a36Sopenharmony_ci				ptname[i] = nocase ? c : fat_tolower(c);
42562306a36Sopenharmony_ci			i++;
42662306a36Sopenharmony_ci			if (c != ' ') {
42762306a36Sopenharmony_ci				name_len = i;
42862306a36Sopenharmony_ci				uni_len  = j;
42962306a36Sopenharmony_ci			}
43062306a36Sopenharmony_ci		} else {
43162306a36Sopenharmony_ci			uni_len = j;
43262306a36Sopenharmony_ci			if (isvfat) {
43362306a36Sopenharmony_ci				int offset = min(chl, MSDOS_NAME-k);
43462306a36Sopenharmony_ci				k += offset;
43562306a36Sopenharmony_ci				i += offset;
43662306a36Sopenharmony_ci			} else {
43762306a36Sopenharmony_ci				for (chi = 0; chi < chl && k < MSDOS_NAME;
43862306a36Sopenharmony_ci				     chi++, i++, k++) {
43962306a36Sopenharmony_ci						ptname[i] = work[k];
44062306a36Sopenharmony_ci				}
44162306a36Sopenharmony_ci			}
44262306a36Sopenharmony_ci			if (chl)
44362306a36Sopenharmony_ci				name_len = i;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (name_len > 0) {
44862306a36Sopenharmony_ci		name_len += dotoffset;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (sbi->options.isvfat) {
45162306a36Sopenharmony_ci			uni_name[uni_len] = 0x0000;
45262306a36Sopenharmony_ci			name_len = fat_uni_to_x8(sb, uni_name, name,
45362306a36Sopenharmony_ci						 FAT_MAX_SHORT_SIZE);
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return name_len;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci * Return values: negative -> error/not found, 0 -> found.
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_ciint fat_search_long(struct inode *inode, const unsigned char *name,
46462306a36Sopenharmony_ci		    int name_len, struct fat_slot_info *sinfo)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
46762306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
46862306a36Sopenharmony_ci	struct buffer_head *bh = NULL;
46962306a36Sopenharmony_ci	struct msdos_dir_entry *de;
47062306a36Sopenharmony_ci	unsigned char nr_slots;
47162306a36Sopenharmony_ci	wchar_t *unicode = NULL;
47262306a36Sopenharmony_ci	unsigned char bufname[FAT_MAX_SHORT_SIZE];
47362306a36Sopenharmony_ci	loff_t cpos = 0;
47462306a36Sopenharmony_ci	int err, len;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	err = -ENOENT;
47762306a36Sopenharmony_ci	while (1) {
47862306a36Sopenharmony_ci		if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
47962306a36Sopenharmony_ci			goto end_of_dir;
48062306a36Sopenharmony_ciparse_record:
48162306a36Sopenharmony_ci		nr_slots = 0;
48262306a36Sopenharmony_ci		if (de->name[0] == DELETED_FLAG)
48362306a36Sopenharmony_ci			continue;
48462306a36Sopenharmony_ci		if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
48562306a36Sopenharmony_ci			continue;
48662306a36Sopenharmony_ci		if (de->attr != ATTR_EXT && IS_FREE(de->name))
48762306a36Sopenharmony_ci			continue;
48862306a36Sopenharmony_ci		if (de->attr == ATTR_EXT) {
48962306a36Sopenharmony_ci			int status = fat_parse_long(inode, &cpos, &bh, &de,
49062306a36Sopenharmony_ci						    &unicode, &nr_slots);
49162306a36Sopenharmony_ci			if (status < 0) {
49262306a36Sopenharmony_ci				err = status;
49362306a36Sopenharmony_ci				goto end_of_dir;
49462306a36Sopenharmony_ci			} else if (status == PARSE_INVALID)
49562306a36Sopenharmony_ci				continue;
49662306a36Sopenharmony_ci			else if (status == PARSE_NOT_LONGNAME)
49762306a36Sopenharmony_ci				goto parse_record;
49862306a36Sopenharmony_ci			else if (status == PARSE_EOF)
49962306a36Sopenharmony_ci				goto end_of_dir;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		/* Never prepend '.' to hidden files here.
50362306a36Sopenharmony_ci		 * That is done only for msdos mounts (and only when
50462306a36Sopenharmony_ci		 * 'dotsOK=yes'); if we are executing here, it is in the
50562306a36Sopenharmony_ci		 * context of a vfat mount.
50662306a36Sopenharmony_ci		 */
50762306a36Sopenharmony_ci		len = fat_parse_short(sb, de, bufname, 0);
50862306a36Sopenharmony_ci		if (len == 0)
50962306a36Sopenharmony_ci			continue;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		/* Compare shortname */
51262306a36Sopenharmony_ci		if (fat_name_match(sbi, name, name_len, bufname, len))
51362306a36Sopenharmony_ci			goto found;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (nr_slots) {
51662306a36Sopenharmony_ci			void *longname = unicode + FAT_MAX_UNI_CHARS;
51762306a36Sopenharmony_ci			int size = PATH_MAX - FAT_MAX_UNI_SIZE;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			/* Compare longname */
52062306a36Sopenharmony_ci			len = fat_uni_to_x8(sb, unicode, longname, size);
52162306a36Sopenharmony_ci			if (fat_name_match(sbi, name, name_len, longname, len))
52262306a36Sopenharmony_ci				goto found;
52362306a36Sopenharmony_ci		}
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cifound:
52762306a36Sopenharmony_ci	nr_slots++;	/* include the de */
52862306a36Sopenharmony_ci	sinfo->slot_off = cpos - nr_slots * sizeof(*de);
52962306a36Sopenharmony_ci	sinfo->nr_slots = nr_slots;
53062306a36Sopenharmony_ci	sinfo->de = de;
53162306a36Sopenharmony_ci	sinfo->bh = bh;
53262306a36Sopenharmony_ci	sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
53362306a36Sopenharmony_ci	err = 0;
53462306a36Sopenharmony_ciend_of_dir:
53562306a36Sopenharmony_ci	if (unicode)
53662306a36Sopenharmony_ci		__putname(unicode);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return err;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_search_long);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistruct fat_ioctl_filldir_callback {
54362306a36Sopenharmony_ci	struct dir_context ctx;
54462306a36Sopenharmony_ci	void __user *dirent;
54562306a36Sopenharmony_ci	int result;
54662306a36Sopenharmony_ci	/* for dir ioctl */
54762306a36Sopenharmony_ci	const char *longname;
54862306a36Sopenharmony_ci	int long_len;
54962306a36Sopenharmony_ci	const char *shortname;
55062306a36Sopenharmony_ci	int short_len;
55162306a36Sopenharmony_ci};
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int __fat_readdir(struct inode *inode, struct file *file,
55462306a36Sopenharmony_ci			 struct dir_context *ctx, int short_only,
55562306a36Sopenharmony_ci			 struct fat_ioctl_filldir_callback *both)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
55862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
55962306a36Sopenharmony_ci	struct buffer_head *bh;
56062306a36Sopenharmony_ci	struct msdos_dir_entry *de;
56162306a36Sopenharmony_ci	unsigned char nr_slots;
56262306a36Sopenharmony_ci	wchar_t *unicode = NULL;
56362306a36Sopenharmony_ci	unsigned char bufname[FAT_MAX_SHORT_SIZE];
56462306a36Sopenharmony_ci	int isvfat = sbi->options.isvfat;
56562306a36Sopenharmony_ci	const char *fill_name = NULL;
56662306a36Sopenharmony_ci	int fake_offset = 0;
56762306a36Sopenharmony_ci	loff_t cpos;
56862306a36Sopenharmony_ci	int short_len = 0, fill_len = 0;
56962306a36Sopenharmony_ci	int ret = 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	mutex_lock(&sbi->s_lock);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	cpos = ctx->pos;
57462306a36Sopenharmony_ci	/* Fake . and .. for the root directory. */
57562306a36Sopenharmony_ci	if (inode->i_ino == MSDOS_ROOT_INO) {
57662306a36Sopenharmony_ci		if (!dir_emit_dots(file, ctx))
57762306a36Sopenharmony_ci			goto out;
57862306a36Sopenharmony_ci		if (ctx->pos == 2) {
57962306a36Sopenharmony_ci			fake_offset = 1;
58062306a36Sopenharmony_ci			cpos = 0;
58162306a36Sopenharmony_ci		}
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	if (cpos & (sizeof(struct msdos_dir_entry) - 1)) {
58462306a36Sopenharmony_ci		ret = -ENOENT;
58562306a36Sopenharmony_ci		goto out;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	bh = NULL;
58962306a36Sopenharmony_ciget_new:
59062306a36Sopenharmony_ci	if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
59162306a36Sopenharmony_ci		goto end_of_dir;
59262306a36Sopenharmony_ciparse_record:
59362306a36Sopenharmony_ci	nr_slots = 0;
59462306a36Sopenharmony_ci	/*
59562306a36Sopenharmony_ci	 * Check for long filename entry, but if short_only, we don't
59662306a36Sopenharmony_ci	 * need to parse long filename.
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	if (isvfat && !short_only) {
59962306a36Sopenharmony_ci		if (de->name[0] == DELETED_FLAG)
60062306a36Sopenharmony_ci			goto record_end;
60162306a36Sopenharmony_ci		if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
60262306a36Sopenharmony_ci			goto record_end;
60362306a36Sopenharmony_ci		if (de->attr != ATTR_EXT && IS_FREE(de->name))
60462306a36Sopenharmony_ci			goto record_end;
60562306a36Sopenharmony_ci	} else {
60662306a36Sopenharmony_ci		if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
60762306a36Sopenharmony_ci			goto record_end;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (isvfat && de->attr == ATTR_EXT) {
61162306a36Sopenharmony_ci		int status = fat_parse_long(inode, &cpos, &bh, &de,
61262306a36Sopenharmony_ci					    &unicode, &nr_slots);
61362306a36Sopenharmony_ci		if (status < 0) {
61462306a36Sopenharmony_ci			bh = NULL;
61562306a36Sopenharmony_ci			ret = status;
61662306a36Sopenharmony_ci			goto end_of_dir;
61762306a36Sopenharmony_ci		} else if (status == PARSE_INVALID)
61862306a36Sopenharmony_ci			goto record_end;
61962306a36Sopenharmony_ci		else if (status == PARSE_NOT_LONGNAME)
62062306a36Sopenharmony_ci			goto parse_record;
62162306a36Sopenharmony_ci		else if (status == PARSE_EOF)
62262306a36Sopenharmony_ci			goto end_of_dir;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		if (nr_slots) {
62562306a36Sopenharmony_ci			void *longname = unicode + FAT_MAX_UNI_CHARS;
62662306a36Sopenharmony_ci			int size = PATH_MAX - FAT_MAX_UNI_SIZE;
62762306a36Sopenharmony_ci			int len = fat_uni_to_x8(sb, unicode, longname, size);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci			fill_name = longname;
63062306a36Sopenharmony_ci			fill_len = len;
63162306a36Sopenharmony_ci			/* !both && !short_only, so we don't need shortname. */
63262306a36Sopenharmony_ci			if (!both)
63362306a36Sopenharmony_ci				goto start_filldir;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci			short_len = fat_parse_short(sb, de, bufname,
63662306a36Sopenharmony_ci						    sbi->options.dotsOK);
63762306a36Sopenharmony_ci			if (short_len == 0)
63862306a36Sopenharmony_ci				goto record_end;
63962306a36Sopenharmony_ci			/* hack for fat_ioctl_filldir() */
64062306a36Sopenharmony_ci			both->longname = fill_name;
64162306a36Sopenharmony_ci			both->long_len = fill_len;
64262306a36Sopenharmony_ci			both->shortname = bufname;
64362306a36Sopenharmony_ci			both->short_len = short_len;
64462306a36Sopenharmony_ci			fill_name = NULL;
64562306a36Sopenharmony_ci			fill_len = 0;
64662306a36Sopenharmony_ci			goto start_filldir;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK);
65162306a36Sopenharmony_ci	if (short_len == 0)
65262306a36Sopenharmony_ci		goto record_end;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	fill_name = bufname;
65562306a36Sopenharmony_ci	fill_len = short_len;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistart_filldir:
65862306a36Sopenharmony_ci	ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry);
65962306a36Sopenharmony_ci	if (fake_offset && ctx->pos < 2)
66062306a36Sopenharmony_ci		ctx->pos = 2;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) {
66362306a36Sopenharmony_ci		if (!dir_emit_dot(file, ctx))
66462306a36Sopenharmony_ci			goto fill_failed;
66562306a36Sopenharmony_ci	} else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
66662306a36Sopenharmony_ci		if (!dir_emit_dotdot(file, ctx))
66762306a36Sopenharmony_ci			goto fill_failed;
66862306a36Sopenharmony_ci	} else {
66962306a36Sopenharmony_ci		unsigned long inum;
67062306a36Sopenharmony_ci		loff_t i_pos = fat_make_i_pos(sb, bh, de);
67162306a36Sopenharmony_ci		struct inode *tmp = fat_iget(sb, i_pos);
67262306a36Sopenharmony_ci		if (tmp) {
67362306a36Sopenharmony_ci			inum = tmp->i_ino;
67462306a36Sopenharmony_ci			iput(tmp);
67562306a36Sopenharmony_ci		} else
67662306a36Sopenharmony_ci			inum = iunique(sb, MSDOS_ROOT_INO);
67762306a36Sopenharmony_ci		if (!dir_emit(ctx, fill_name, fill_len, inum,
67862306a36Sopenharmony_ci			    (de->attr & ATTR_DIR) ? DT_DIR : DT_REG))
67962306a36Sopenharmony_ci			goto fill_failed;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cirecord_end:
68362306a36Sopenharmony_ci	fake_offset = 0;
68462306a36Sopenharmony_ci	ctx->pos = cpos;
68562306a36Sopenharmony_ci	goto get_new;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ciend_of_dir:
68862306a36Sopenharmony_ci	if (fake_offset && cpos < 2)
68962306a36Sopenharmony_ci		ctx->pos = 2;
69062306a36Sopenharmony_ci	else
69162306a36Sopenharmony_ci		ctx->pos = cpos;
69262306a36Sopenharmony_cifill_failed:
69362306a36Sopenharmony_ci	brelse(bh);
69462306a36Sopenharmony_ci	if (unicode)
69562306a36Sopenharmony_ci		__putname(unicode);
69662306a36Sopenharmony_ciout:
69762306a36Sopenharmony_ci	mutex_unlock(&sbi->s_lock);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return ret;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic int fat_readdir(struct file *file, struct dir_context *ctx)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	return __fat_readdir(file_inode(file), file, ctx, 0, NULL);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type)			   \
70862306a36Sopenharmony_cistatic bool func(struct dir_context *ctx, const char *name, int name_len,  \
70962306a36Sopenharmony_ci			     loff_t offset, u64 ino, unsigned int d_type)  \
71062306a36Sopenharmony_ci{									   \
71162306a36Sopenharmony_ci	struct fat_ioctl_filldir_callback *buf =			   \
71262306a36Sopenharmony_ci		container_of(ctx, struct fat_ioctl_filldir_callback, ctx); \
71362306a36Sopenharmony_ci	struct dirent_type __user *d1 = buf->dirent;			   \
71462306a36Sopenharmony_ci	struct dirent_type __user *d2 = d1 + 1;				   \
71562306a36Sopenharmony_ci									   \
71662306a36Sopenharmony_ci	if (buf->result)						   \
71762306a36Sopenharmony_ci		return false;						   \
71862306a36Sopenharmony_ci	buf->result++;							   \
71962306a36Sopenharmony_ci									   \
72062306a36Sopenharmony_ci	if (name != NULL) {						   \
72162306a36Sopenharmony_ci		/* dirent has only short name */			   \
72262306a36Sopenharmony_ci		if (name_len >= sizeof(d1->d_name))			   \
72362306a36Sopenharmony_ci			name_len = sizeof(d1->d_name) - 1;		   \
72462306a36Sopenharmony_ci									   \
72562306a36Sopenharmony_ci		if (put_user(0, &d2->d_name[0])			||	   \
72662306a36Sopenharmony_ci		    put_user(0, &d2->d_reclen)			||	   \
72762306a36Sopenharmony_ci		    copy_to_user(d1->d_name, name, name_len)	||	   \
72862306a36Sopenharmony_ci		    put_user(0, d1->d_name + name_len)		||	   \
72962306a36Sopenharmony_ci		    put_user(name_len, &d1->d_reclen))			   \
73062306a36Sopenharmony_ci			goto efault;					   \
73162306a36Sopenharmony_ci	} else {							   \
73262306a36Sopenharmony_ci		/* dirent has short and long name */			   \
73362306a36Sopenharmony_ci		const char *longname = buf->longname;			   \
73462306a36Sopenharmony_ci		int long_len = buf->long_len;				   \
73562306a36Sopenharmony_ci		const char *shortname = buf->shortname;			   \
73662306a36Sopenharmony_ci		int short_len = buf->short_len;				   \
73762306a36Sopenharmony_ci									   \
73862306a36Sopenharmony_ci		if (long_len >= sizeof(d1->d_name))			   \
73962306a36Sopenharmony_ci			long_len = sizeof(d1->d_name) - 1;		   \
74062306a36Sopenharmony_ci		if (short_len >= sizeof(d1->d_name))			   \
74162306a36Sopenharmony_ci			short_len = sizeof(d1->d_name) - 1;		   \
74262306a36Sopenharmony_ci									   \
74362306a36Sopenharmony_ci		if (copy_to_user(d2->d_name, longname, long_len)	|| \
74462306a36Sopenharmony_ci		    put_user(0, d2->d_name + long_len)			|| \
74562306a36Sopenharmony_ci		    put_user(long_len, &d2->d_reclen)			|| \
74662306a36Sopenharmony_ci		    put_user(ino, &d2->d_ino)				|| \
74762306a36Sopenharmony_ci		    put_user(offset, &d2->d_off)			|| \
74862306a36Sopenharmony_ci		    copy_to_user(d1->d_name, shortname, short_len)	|| \
74962306a36Sopenharmony_ci		    put_user(0, d1->d_name + short_len)			|| \
75062306a36Sopenharmony_ci		    put_user(short_len, &d1->d_reclen))			   \
75162306a36Sopenharmony_ci			goto efault;					   \
75262306a36Sopenharmony_ci	}								   \
75362306a36Sopenharmony_ci	return true;							   \
75462306a36Sopenharmony_ciefault:									   \
75562306a36Sopenharmony_ci	buf->result = -EFAULT;						   \
75662306a36Sopenharmony_ci	return false;							   \
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ciFAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, __fat_dirent)
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int fat_ioctl_readdir(struct inode *inode, struct file *file,
76262306a36Sopenharmony_ci			     void __user *dirent, filldir_t filldir,
76362306a36Sopenharmony_ci			     int short_only, int both)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct fat_ioctl_filldir_callback buf = {
76662306a36Sopenharmony_ci		.ctx.actor = filldir,
76762306a36Sopenharmony_ci		.dirent = dirent
76862306a36Sopenharmony_ci	};
76962306a36Sopenharmony_ci	int ret;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	buf.dirent = dirent;
77262306a36Sopenharmony_ci	buf.result = 0;
77362306a36Sopenharmony_ci	inode_lock_shared(inode);
77462306a36Sopenharmony_ci	buf.ctx.pos = file->f_pos;
77562306a36Sopenharmony_ci	ret = -ENOENT;
77662306a36Sopenharmony_ci	if (!IS_DEADDIR(inode)) {
77762306a36Sopenharmony_ci		ret = __fat_readdir(inode, file, &buf.ctx,
77862306a36Sopenharmony_ci				    short_only, both ? &buf : NULL);
77962306a36Sopenharmony_ci		file->f_pos = buf.ctx.pos;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	inode_unlock_shared(inode);
78262306a36Sopenharmony_ci	if (ret >= 0)
78362306a36Sopenharmony_ci		ret = buf.result;
78462306a36Sopenharmony_ci	return ret;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic long fat_dir_ioctl(struct file *filp, unsigned int cmd,
78862306a36Sopenharmony_ci			  unsigned long arg)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
79162306a36Sopenharmony_ci	struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg;
79262306a36Sopenharmony_ci	int short_only, both;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	switch (cmd) {
79562306a36Sopenharmony_ci	case VFAT_IOCTL_READDIR_SHORT:
79662306a36Sopenharmony_ci		short_only = 1;
79762306a36Sopenharmony_ci		both = 0;
79862306a36Sopenharmony_ci		break;
79962306a36Sopenharmony_ci	case VFAT_IOCTL_READDIR_BOTH:
80062306a36Sopenharmony_ci		short_only = 0;
80162306a36Sopenharmony_ci		both = 1;
80262306a36Sopenharmony_ci		break;
80362306a36Sopenharmony_ci	default:
80462306a36Sopenharmony_ci		return fat_generic_ioctl(filp, cmd, arg);
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/*
80862306a36Sopenharmony_ci	 * Yes, we don't need this put_user() absolutely. However old
80962306a36Sopenharmony_ci	 * code didn't return the right value. So, app use this value,
81062306a36Sopenharmony_ci	 * in order to check whether it is EOF.
81162306a36Sopenharmony_ci	 */
81262306a36Sopenharmony_ci	if (put_user(0, &d1->d_reclen))
81362306a36Sopenharmony_ci		return -EFAULT;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir,
81662306a36Sopenharmony_ci				 short_only, both);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
82062306a36Sopenharmony_ci#define	VFAT_IOCTL_READDIR_BOTH32	_IOR('r', 1, struct compat_dirent[2])
82162306a36Sopenharmony_ci#define	VFAT_IOCTL_READDIR_SHORT32	_IOR('r', 2, struct compat_dirent[2])
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ciFAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent)
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
82662306a36Sopenharmony_ci				 unsigned long arg)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
82962306a36Sopenharmony_ci	struct compat_dirent __user *d1 = compat_ptr(arg);
83062306a36Sopenharmony_ci	int short_only, both;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	switch (cmd) {
83362306a36Sopenharmony_ci	case VFAT_IOCTL_READDIR_SHORT32:
83462306a36Sopenharmony_ci		short_only = 1;
83562306a36Sopenharmony_ci		both = 0;
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci	case VFAT_IOCTL_READDIR_BOTH32:
83862306a36Sopenharmony_ci		short_only = 0;
83962306a36Sopenharmony_ci		both = 1;
84062306a36Sopenharmony_ci		break;
84162306a36Sopenharmony_ci	default:
84262306a36Sopenharmony_ci		return fat_generic_ioctl(filp, cmd, (unsigned long)arg);
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	/*
84662306a36Sopenharmony_ci	 * Yes, we don't need this put_user() absolutely. However old
84762306a36Sopenharmony_ci	 * code didn't return the right value. So, app use this value,
84862306a36Sopenharmony_ci	 * in order to check whether it is EOF.
84962306a36Sopenharmony_ci	 */
85062306a36Sopenharmony_ci	if (put_user(0, &d1->d_reclen))
85162306a36Sopenharmony_ci		return -EFAULT;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir,
85462306a36Sopenharmony_ci				 short_only, both);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ciconst struct file_operations fat_dir_operations = {
85962306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
86062306a36Sopenharmony_ci	.read		= generic_read_dir,
86162306a36Sopenharmony_ci	.iterate_shared	= fat_readdir,
86262306a36Sopenharmony_ci	.unlocked_ioctl	= fat_dir_ioctl,
86362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
86462306a36Sopenharmony_ci	.compat_ioctl	= fat_compat_dir_ioctl,
86562306a36Sopenharmony_ci#endif
86662306a36Sopenharmony_ci	.fsync		= fat_file_fsync,
86762306a36Sopenharmony_ci};
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int fat_get_short_entry(struct inode *dir, loff_t *pos,
87062306a36Sopenharmony_ci			       struct buffer_head **bh,
87162306a36Sopenharmony_ci			       struct msdos_dir_entry **de)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	while (fat_get_entry(dir, pos, bh, de) >= 0) {
87462306a36Sopenharmony_ci		/* free entry or long name entry or volume label */
87562306a36Sopenharmony_ci		if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME))
87662306a36Sopenharmony_ci			return 0;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	return -ENOENT;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci/*
88262306a36Sopenharmony_ci * The ".." entry can not provide the "struct fat_slot_info" information
88362306a36Sopenharmony_ci * for inode, nor a usable i_pos. So, this function provides some information
88462306a36Sopenharmony_ci * only.
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci * Since this function walks through the on-disk inodes within a directory,
88762306a36Sopenharmony_ci * callers are responsible for taking any locks necessary to prevent the
88862306a36Sopenharmony_ci * directory from changing.
88962306a36Sopenharmony_ci */
89062306a36Sopenharmony_ciint fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
89162306a36Sopenharmony_ci			 struct msdos_dir_entry **de)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	loff_t offset = 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	*de = NULL;
89662306a36Sopenharmony_ci	while (fat_get_short_entry(dir, &offset, bh, de) >= 0) {
89762306a36Sopenharmony_ci		if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME))
89862306a36Sopenharmony_ci			return 0;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci	return -ENOENT;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_get_dotdot_entry);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/* See if directory is empty */
90562306a36Sopenharmony_ciint fat_dir_empty(struct inode *dir)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct buffer_head *bh;
90862306a36Sopenharmony_ci	struct msdos_dir_entry *de;
90962306a36Sopenharmony_ci	loff_t cpos;
91062306a36Sopenharmony_ci	int result = 0;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	bh = NULL;
91362306a36Sopenharmony_ci	cpos = 0;
91462306a36Sopenharmony_ci	while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) {
91562306a36Sopenharmony_ci		if (strncmp(de->name, MSDOS_DOT   , MSDOS_NAME) &&
91662306a36Sopenharmony_ci		    strncmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
91762306a36Sopenharmony_ci			result = -ENOTEMPTY;
91862306a36Sopenharmony_ci			break;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci	brelse(bh);
92262306a36Sopenharmony_ci	return result;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_dir_empty);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci * fat_subdirs counts the number of sub-directories of dir. It can be run
92862306a36Sopenharmony_ci * on directories being created.
92962306a36Sopenharmony_ci */
93062306a36Sopenharmony_ciint fat_subdirs(struct inode *dir)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct buffer_head *bh;
93362306a36Sopenharmony_ci	struct msdos_dir_entry *de;
93462306a36Sopenharmony_ci	loff_t cpos;
93562306a36Sopenharmony_ci	int count = 0;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	bh = NULL;
93862306a36Sopenharmony_ci	cpos = 0;
93962306a36Sopenharmony_ci	while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) {
94062306a36Sopenharmony_ci		if (de->attr & ATTR_DIR)
94162306a36Sopenharmony_ci			count++;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	brelse(bh);
94462306a36Sopenharmony_ci	return count;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/*
94862306a36Sopenharmony_ci * Scans a directory for a given file (name points to its formatted name).
94962306a36Sopenharmony_ci * Returns an error code or zero.
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_ciint fat_scan(struct inode *dir, const unsigned char *name,
95262306a36Sopenharmony_ci	     struct fat_slot_info *sinfo)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	sinfo->slot_off = 0;
95762306a36Sopenharmony_ci	sinfo->bh = NULL;
95862306a36Sopenharmony_ci	while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh,
95962306a36Sopenharmony_ci				   &sinfo->de) >= 0) {
96062306a36Sopenharmony_ci		if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) {
96162306a36Sopenharmony_ci			sinfo->slot_off -= sizeof(*sinfo->de);
96262306a36Sopenharmony_ci			sinfo->nr_slots = 1;
96362306a36Sopenharmony_ci			sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
96462306a36Sopenharmony_ci			return 0;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci	return -ENOENT;
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_scan);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/*
97262306a36Sopenharmony_ci * Scans a directory for a given logstart.
97362306a36Sopenharmony_ci * Returns an error code or zero.
97462306a36Sopenharmony_ci */
97562306a36Sopenharmony_ciint fat_scan_logstart(struct inode *dir, int i_logstart,
97662306a36Sopenharmony_ci		      struct fat_slot_info *sinfo)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	sinfo->slot_off = 0;
98162306a36Sopenharmony_ci	sinfo->bh = NULL;
98262306a36Sopenharmony_ci	while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh,
98362306a36Sopenharmony_ci				   &sinfo->de) >= 0) {
98462306a36Sopenharmony_ci		if (fat_get_start(MSDOS_SB(sb), sinfo->de) == i_logstart) {
98562306a36Sopenharmony_ci			sinfo->slot_off -= sizeof(*sinfo->de);
98662306a36Sopenharmony_ci			sinfo->nr_slots = 1;
98762306a36Sopenharmony_ci			sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
98862306a36Sopenharmony_ci			return 0;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci	return -ENOENT;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
99762306a36Sopenharmony_ci	struct buffer_head *bh;
99862306a36Sopenharmony_ci	struct msdos_dir_entry *de, *endp;
99962306a36Sopenharmony_ci	int err = 0, orig_slots;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	while (nr_slots) {
100262306a36Sopenharmony_ci		bh = NULL;
100362306a36Sopenharmony_ci		if (fat_get_entry(dir, &pos, &bh, &de) < 0) {
100462306a36Sopenharmony_ci			err = -EIO;
100562306a36Sopenharmony_ci			break;
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		orig_slots = nr_slots;
100962306a36Sopenharmony_ci		endp = (struct msdos_dir_entry *)(bh->b_data + sb->s_blocksize);
101062306a36Sopenharmony_ci		while (nr_slots && de < endp) {
101162306a36Sopenharmony_ci			de->name[0] = DELETED_FLAG;
101262306a36Sopenharmony_ci			de++;
101362306a36Sopenharmony_ci			nr_slots--;
101462306a36Sopenharmony_ci		}
101562306a36Sopenharmony_ci		mark_buffer_dirty_inode(bh, dir);
101662306a36Sopenharmony_ci		if (IS_DIRSYNC(dir))
101762306a36Sopenharmony_ci			err = sync_dirty_buffer(bh);
101862306a36Sopenharmony_ci		brelse(bh);
101962306a36Sopenharmony_ci		if (err)
102062306a36Sopenharmony_ci			break;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		/* pos is *next* de's position, so this does `- sizeof(de)' */
102362306a36Sopenharmony_ci		pos += ((orig_slots - nr_slots) * sizeof(*de)) - sizeof(*de);
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return err;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ciint fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
103262306a36Sopenharmony_ci	struct msdos_dir_entry *de;
103362306a36Sopenharmony_ci	struct buffer_head *bh;
103462306a36Sopenharmony_ci	int err = 0, nr_slots;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/*
103762306a36Sopenharmony_ci	 * First stage: Remove the shortname. By this, the directory
103862306a36Sopenharmony_ci	 * entry is removed.
103962306a36Sopenharmony_ci	 */
104062306a36Sopenharmony_ci	nr_slots = sinfo->nr_slots;
104162306a36Sopenharmony_ci	de = sinfo->de;
104262306a36Sopenharmony_ci	sinfo->de = NULL;
104362306a36Sopenharmony_ci	bh = sinfo->bh;
104462306a36Sopenharmony_ci	sinfo->bh = NULL;
104562306a36Sopenharmony_ci	while (nr_slots && de >= (struct msdos_dir_entry *)bh->b_data) {
104662306a36Sopenharmony_ci		de->name[0] = DELETED_FLAG;
104762306a36Sopenharmony_ci		de--;
104862306a36Sopenharmony_ci		nr_slots--;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci	mark_buffer_dirty_inode(bh, dir);
105162306a36Sopenharmony_ci	if (IS_DIRSYNC(dir))
105262306a36Sopenharmony_ci		err = sync_dirty_buffer(bh);
105362306a36Sopenharmony_ci	brelse(bh);
105462306a36Sopenharmony_ci	if (err)
105562306a36Sopenharmony_ci		return err;
105662306a36Sopenharmony_ci	inode_inc_iversion(dir);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (nr_slots) {
105962306a36Sopenharmony_ci		/*
106062306a36Sopenharmony_ci		 * Second stage: remove the remaining longname slots.
106162306a36Sopenharmony_ci		 * (This directory entry is already removed, and so return
106262306a36Sopenharmony_ci		 * the success)
106362306a36Sopenharmony_ci		 */
106462306a36Sopenharmony_ci		err = __fat_remove_entries(dir, sinfo->slot_off, nr_slots);
106562306a36Sopenharmony_ci		if (err) {
106662306a36Sopenharmony_ci			fat_msg(sb, KERN_WARNING,
106762306a36Sopenharmony_ci			       "Couldn't remove the long name slots");
106862306a36Sopenharmony_ci		}
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	fat_truncate_time(dir, NULL, S_ATIME|S_MTIME);
107262306a36Sopenharmony_ci	if (IS_DIRSYNC(dir))
107362306a36Sopenharmony_ci		(void)fat_sync_inode(dir);
107462306a36Sopenharmony_ci	else
107562306a36Sopenharmony_ci		mark_inode_dirty(dir);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	return 0;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_remove_entries);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
108262306a36Sopenharmony_ci			      struct buffer_head **bhs, int nr_bhs)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
108562306a36Sopenharmony_ci	sector_t last_blknr = blknr + MSDOS_SB(sb)->sec_per_clus;
108662306a36Sopenharmony_ci	int err, i, n;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/* Zeroing the unused blocks on this cluster */
108962306a36Sopenharmony_ci	blknr += nr_used;
109062306a36Sopenharmony_ci	n = nr_used;
109162306a36Sopenharmony_ci	while (blknr < last_blknr) {
109262306a36Sopenharmony_ci		bhs[n] = sb_getblk(sb, blknr);
109362306a36Sopenharmony_ci		if (!bhs[n]) {
109462306a36Sopenharmony_ci			err = -ENOMEM;
109562306a36Sopenharmony_ci			goto error;
109662306a36Sopenharmony_ci		}
109762306a36Sopenharmony_ci		/* Avoid race with userspace read via bdev */
109862306a36Sopenharmony_ci		lock_buffer(bhs[n]);
109962306a36Sopenharmony_ci		memset(bhs[n]->b_data, 0, sb->s_blocksize);
110062306a36Sopenharmony_ci		set_buffer_uptodate(bhs[n]);
110162306a36Sopenharmony_ci		unlock_buffer(bhs[n]);
110262306a36Sopenharmony_ci		mark_buffer_dirty_inode(bhs[n], dir);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci		n++;
110562306a36Sopenharmony_ci		blknr++;
110662306a36Sopenharmony_ci		if (n == nr_bhs) {
110762306a36Sopenharmony_ci			if (IS_DIRSYNC(dir)) {
110862306a36Sopenharmony_ci				err = fat_sync_bhs(bhs, n);
110962306a36Sopenharmony_ci				if (err)
111062306a36Sopenharmony_ci					goto error;
111162306a36Sopenharmony_ci			}
111262306a36Sopenharmony_ci			for (i = 0; i < n; i++)
111362306a36Sopenharmony_ci				brelse(bhs[i]);
111462306a36Sopenharmony_ci			n = 0;
111562306a36Sopenharmony_ci		}
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci	if (IS_DIRSYNC(dir)) {
111862306a36Sopenharmony_ci		err = fat_sync_bhs(bhs, n);
111962306a36Sopenharmony_ci		if (err)
112062306a36Sopenharmony_ci			goto error;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci	for (i = 0; i < n; i++)
112362306a36Sopenharmony_ci		brelse(bhs[i]);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return 0;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cierror:
112862306a36Sopenharmony_ci	for (i = 0; i < n; i++)
112962306a36Sopenharmony_ci		bforget(bhs[i]);
113062306a36Sopenharmony_ci	return err;
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ciint fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
113662306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
113762306a36Sopenharmony_ci	struct buffer_head *bhs[MAX_BUF_PER_PAGE];
113862306a36Sopenharmony_ci	struct msdos_dir_entry *de;
113962306a36Sopenharmony_ci	sector_t blknr;
114062306a36Sopenharmony_ci	__le16 date, time;
114162306a36Sopenharmony_ci	u8 time_cs;
114262306a36Sopenharmony_ci	int err, cluster;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	err = fat_alloc_clusters(dir, &cluster, 1);
114562306a36Sopenharmony_ci	if (err)
114662306a36Sopenharmony_ci		goto error;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	blknr = fat_clus_to_blknr(sbi, cluster);
114962306a36Sopenharmony_ci	bhs[0] = sb_getblk(sb, blknr);
115062306a36Sopenharmony_ci	if (!bhs[0]) {
115162306a36Sopenharmony_ci		err = -ENOMEM;
115262306a36Sopenharmony_ci		goto error_free;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	de = (struct msdos_dir_entry *)bhs[0]->b_data;
115862306a36Sopenharmony_ci	/* Avoid race with userspace read via bdev */
115962306a36Sopenharmony_ci	lock_buffer(bhs[0]);
116062306a36Sopenharmony_ci	/* filling the new directory slots ("." and ".." entries) */
116162306a36Sopenharmony_ci	memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME);
116262306a36Sopenharmony_ci	memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME);
116362306a36Sopenharmony_ci	de->attr = de[1].attr = ATTR_DIR;
116462306a36Sopenharmony_ci	de[0].lcase = de[1].lcase = 0;
116562306a36Sopenharmony_ci	de[0].time = de[1].time = time;
116662306a36Sopenharmony_ci	de[0].date = de[1].date = date;
116762306a36Sopenharmony_ci	if (sbi->options.isvfat) {
116862306a36Sopenharmony_ci		/* extra timestamps */
116962306a36Sopenharmony_ci		de[0].ctime = de[1].ctime = time;
117062306a36Sopenharmony_ci		de[0].ctime_cs = de[1].ctime_cs = time_cs;
117162306a36Sopenharmony_ci		de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date;
117262306a36Sopenharmony_ci	} else {
117362306a36Sopenharmony_ci		de[0].ctime = de[1].ctime = 0;
117462306a36Sopenharmony_ci		de[0].ctime_cs = de[1].ctime_cs = 0;
117562306a36Sopenharmony_ci		de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci	fat_set_start(&de[0], cluster);
117862306a36Sopenharmony_ci	fat_set_start(&de[1], MSDOS_I(dir)->i_logstart);
117962306a36Sopenharmony_ci	de[0].size = de[1].size = 0;
118062306a36Sopenharmony_ci	memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
118162306a36Sopenharmony_ci	set_buffer_uptodate(bhs[0]);
118262306a36Sopenharmony_ci	unlock_buffer(bhs[0]);
118362306a36Sopenharmony_ci	mark_buffer_dirty_inode(bhs[0], dir);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
118662306a36Sopenharmony_ci	if (err)
118762306a36Sopenharmony_ci		goto error_free;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	return cluster;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cierror_free:
119262306a36Sopenharmony_ci	fat_free_clusters(dir, cluster);
119362306a36Sopenharmony_cierror:
119462306a36Sopenharmony_ci	return err;
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_alloc_new_dir);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
119962306a36Sopenharmony_ci			       int *nr_cluster, struct msdos_dir_entry **de,
120062306a36Sopenharmony_ci			       struct buffer_head **bh, loff_t *i_pos)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
120362306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
120462306a36Sopenharmony_ci	struct buffer_head *bhs[MAX_BUF_PER_PAGE];
120562306a36Sopenharmony_ci	sector_t blknr, start_blknr, last_blknr;
120662306a36Sopenharmony_ci	unsigned long size, copy;
120762306a36Sopenharmony_ci	int err, i, n, offset, cluster[2];
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/*
121062306a36Sopenharmony_ci	 * The minimum cluster size is 512bytes, and maximum entry
121162306a36Sopenharmony_ci	 * size is 32*slots (672bytes).  So, iff the cluster size is
121262306a36Sopenharmony_ci	 * 512bytes, we may need two clusters.
121362306a36Sopenharmony_ci	 */
121462306a36Sopenharmony_ci	size = nr_slots * sizeof(struct msdos_dir_entry);
121562306a36Sopenharmony_ci	*nr_cluster = (size + (sbi->cluster_size - 1)) >> sbi->cluster_bits;
121662306a36Sopenharmony_ci	BUG_ON(*nr_cluster > 2);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	err = fat_alloc_clusters(dir, cluster, *nr_cluster);
121962306a36Sopenharmony_ci	if (err)
122062306a36Sopenharmony_ci		goto error;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/*
122362306a36Sopenharmony_ci	 * First stage: Fill the directory entry.  NOTE: This cluster
122462306a36Sopenharmony_ci	 * is not referenced from any inode yet, so updates order is
122562306a36Sopenharmony_ci	 * not important.
122662306a36Sopenharmony_ci	 */
122762306a36Sopenharmony_ci	i = n = copy = 0;
122862306a36Sopenharmony_ci	do {
122962306a36Sopenharmony_ci		start_blknr = blknr = fat_clus_to_blknr(sbi, cluster[i]);
123062306a36Sopenharmony_ci		last_blknr = start_blknr + sbi->sec_per_clus;
123162306a36Sopenharmony_ci		while (blknr < last_blknr) {
123262306a36Sopenharmony_ci			bhs[n] = sb_getblk(sb, blknr);
123362306a36Sopenharmony_ci			if (!bhs[n]) {
123462306a36Sopenharmony_ci				err = -ENOMEM;
123562306a36Sopenharmony_ci				goto error_nomem;
123662306a36Sopenharmony_ci			}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci			/* fill the directory entry */
123962306a36Sopenharmony_ci			copy = min(size, sb->s_blocksize);
124062306a36Sopenharmony_ci			/* Avoid race with userspace read via bdev */
124162306a36Sopenharmony_ci			lock_buffer(bhs[n]);
124262306a36Sopenharmony_ci			memcpy(bhs[n]->b_data, slots, copy);
124362306a36Sopenharmony_ci			set_buffer_uptodate(bhs[n]);
124462306a36Sopenharmony_ci			unlock_buffer(bhs[n]);
124562306a36Sopenharmony_ci			mark_buffer_dirty_inode(bhs[n], dir);
124662306a36Sopenharmony_ci			slots += copy;
124762306a36Sopenharmony_ci			size -= copy;
124862306a36Sopenharmony_ci			if (!size)
124962306a36Sopenharmony_ci				break;
125062306a36Sopenharmony_ci			n++;
125162306a36Sopenharmony_ci			blknr++;
125262306a36Sopenharmony_ci		}
125362306a36Sopenharmony_ci	} while (++i < *nr_cluster);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	memset(bhs[n]->b_data + copy, 0, sb->s_blocksize - copy);
125662306a36Sopenharmony_ci	offset = copy - sizeof(struct msdos_dir_entry);
125762306a36Sopenharmony_ci	get_bh(bhs[n]);
125862306a36Sopenharmony_ci	*bh = bhs[n];
125962306a36Sopenharmony_ci	*de = (struct msdos_dir_entry *)((*bh)->b_data + offset);
126062306a36Sopenharmony_ci	*i_pos = fat_make_i_pos(sb, *bh, *de);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* Second stage: clear the rest of cluster, and write outs */
126362306a36Sopenharmony_ci	err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE);
126462306a36Sopenharmony_ci	if (err)
126562306a36Sopenharmony_ci		goto error_free;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return cluster[0];
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cierror_free:
127062306a36Sopenharmony_ci	brelse(*bh);
127162306a36Sopenharmony_ci	*bh = NULL;
127262306a36Sopenharmony_ci	n = 0;
127362306a36Sopenharmony_cierror_nomem:
127462306a36Sopenharmony_ci	for (i = 0; i < n; i++)
127562306a36Sopenharmony_ci		bforget(bhs[i]);
127662306a36Sopenharmony_ci	fat_free_clusters(dir, cluster[0]);
127762306a36Sopenharmony_cierror:
127862306a36Sopenharmony_ci	return err;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ciint fat_add_entries(struct inode *dir, void *slots, int nr_slots,
128262306a36Sopenharmony_ci		    struct fat_slot_info *sinfo)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
128562306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
128662306a36Sopenharmony_ci	struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */
128762306a36Sopenharmony_ci	struct msdos_dir_entry *de;
128862306a36Sopenharmony_ci	int err, free_slots, i, nr_bhs;
128962306a36Sopenharmony_ci	loff_t pos, i_pos;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	sinfo->nr_slots = nr_slots;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* First stage: search free directory entries */
129462306a36Sopenharmony_ci	free_slots = nr_bhs = 0;
129562306a36Sopenharmony_ci	bh = prev = NULL;
129662306a36Sopenharmony_ci	pos = 0;
129762306a36Sopenharmony_ci	err = -ENOSPC;
129862306a36Sopenharmony_ci	while (fat_get_entry(dir, &pos, &bh, &de) > -1) {
129962306a36Sopenharmony_ci		/* check the maximum size of directory */
130062306a36Sopenharmony_ci		if (pos >= FAT_MAX_DIR_SIZE)
130162306a36Sopenharmony_ci			goto error;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		if (IS_FREE(de->name)) {
130462306a36Sopenharmony_ci			if (prev != bh) {
130562306a36Sopenharmony_ci				get_bh(bh);
130662306a36Sopenharmony_ci				bhs[nr_bhs] = prev = bh;
130762306a36Sopenharmony_ci				nr_bhs++;
130862306a36Sopenharmony_ci			}
130962306a36Sopenharmony_ci			free_slots++;
131062306a36Sopenharmony_ci			if (free_slots == nr_slots)
131162306a36Sopenharmony_ci				goto found;
131262306a36Sopenharmony_ci		} else {
131362306a36Sopenharmony_ci			for (i = 0; i < nr_bhs; i++)
131462306a36Sopenharmony_ci				brelse(bhs[i]);
131562306a36Sopenharmony_ci			prev = NULL;
131662306a36Sopenharmony_ci			free_slots = nr_bhs = 0;
131762306a36Sopenharmony_ci		}
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci	if (dir->i_ino == MSDOS_ROOT_INO) {
132062306a36Sopenharmony_ci		if (!is_fat32(sbi))
132162306a36Sopenharmony_ci			goto error;
132262306a36Sopenharmony_ci	} else if (MSDOS_I(dir)->i_start == 0) {
132362306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "Corrupted directory (i_pos %lld)",
132462306a36Sopenharmony_ci		       MSDOS_I(dir)->i_pos);
132562306a36Sopenharmony_ci		err = -EIO;
132662306a36Sopenharmony_ci		goto error;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cifound:
133062306a36Sopenharmony_ci	err = 0;
133162306a36Sopenharmony_ci	pos -= free_slots * sizeof(*de);
133262306a36Sopenharmony_ci	nr_slots -= free_slots;
133362306a36Sopenharmony_ci	if (free_slots) {
133462306a36Sopenharmony_ci		/*
133562306a36Sopenharmony_ci		 * Second stage: filling the free entries with new entries.
133662306a36Sopenharmony_ci		 * NOTE: If this slots has shortname, first, we write
133762306a36Sopenharmony_ci		 * the long name slots, then write the short name.
133862306a36Sopenharmony_ci		 */
133962306a36Sopenharmony_ci		int size = free_slots * sizeof(*de);
134062306a36Sopenharmony_ci		int offset = pos & (sb->s_blocksize - 1);
134162306a36Sopenharmony_ci		int long_bhs = nr_bhs - (nr_slots == 0);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		/* Fill the long name slots. */
134462306a36Sopenharmony_ci		for (i = 0; i < long_bhs; i++) {
134562306a36Sopenharmony_ci			int copy = min_t(int, sb->s_blocksize - offset, size);
134662306a36Sopenharmony_ci			memcpy(bhs[i]->b_data + offset, slots, copy);
134762306a36Sopenharmony_ci			mark_buffer_dirty_inode(bhs[i], dir);
134862306a36Sopenharmony_ci			offset = 0;
134962306a36Sopenharmony_ci			slots += copy;
135062306a36Sopenharmony_ci			size -= copy;
135162306a36Sopenharmony_ci		}
135262306a36Sopenharmony_ci		if (long_bhs && IS_DIRSYNC(dir))
135362306a36Sopenharmony_ci			err = fat_sync_bhs(bhs, long_bhs);
135462306a36Sopenharmony_ci		if (!err && i < nr_bhs) {
135562306a36Sopenharmony_ci			/* Fill the short name slot. */
135662306a36Sopenharmony_ci			int copy = min_t(int, sb->s_blocksize - offset, size);
135762306a36Sopenharmony_ci			memcpy(bhs[i]->b_data + offset, slots, copy);
135862306a36Sopenharmony_ci			mark_buffer_dirty_inode(bhs[i], dir);
135962306a36Sopenharmony_ci			if (IS_DIRSYNC(dir))
136062306a36Sopenharmony_ci				err = sync_dirty_buffer(bhs[i]);
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci		for (i = 0; i < nr_bhs; i++)
136362306a36Sopenharmony_ci			brelse(bhs[i]);
136462306a36Sopenharmony_ci		if (err)
136562306a36Sopenharmony_ci			goto error_remove;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (nr_slots) {
136962306a36Sopenharmony_ci		int cluster, nr_cluster;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		/*
137262306a36Sopenharmony_ci		 * Third stage: allocate the cluster for new entries.
137362306a36Sopenharmony_ci		 * And initialize the cluster with new entries, then
137462306a36Sopenharmony_ci		 * add the cluster to dir.
137562306a36Sopenharmony_ci		 */
137662306a36Sopenharmony_ci		cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster,
137762306a36Sopenharmony_ci					      &de, &bh, &i_pos);
137862306a36Sopenharmony_ci		if (cluster < 0) {
137962306a36Sopenharmony_ci			err = cluster;
138062306a36Sopenharmony_ci			goto error_remove;
138162306a36Sopenharmony_ci		}
138262306a36Sopenharmony_ci		err = fat_chain_add(dir, cluster, nr_cluster);
138362306a36Sopenharmony_ci		if (err) {
138462306a36Sopenharmony_ci			fat_free_clusters(dir, cluster);
138562306a36Sopenharmony_ci			goto error_remove;
138662306a36Sopenharmony_ci		}
138762306a36Sopenharmony_ci		if (dir->i_size & (sbi->cluster_size - 1)) {
138862306a36Sopenharmony_ci			fat_fs_error(sb, "Odd directory size");
138962306a36Sopenharmony_ci			dir->i_size = (dir->i_size + sbi->cluster_size - 1)
139062306a36Sopenharmony_ci				& ~((loff_t)sbi->cluster_size - 1);
139162306a36Sopenharmony_ci		}
139262306a36Sopenharmony_ci		dir->i_size += nr_cluster << sbi->cluster_bits;
139362306a36Sopenharmony_ci		MSDOS_I(dir)->mmu_private += nr_cluster << sbi->cluster_bits;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci	sinfo->slot_off = pos;
139662306a36Sopenharmony_ci	sinfo->de = de;
139762306a36Sopenharmony_ci	sinfo->bh = bh;
139862306a36Sopenharmony_ci	sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	return 0;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cierror:
140362306a36Sopenharmony_ci	brelse(bh);
140462306a36Sopenharmony_ci	for (i = 0; i < nr_bhs; i++)
140562306a36Sopenharmony_ci		brelse(bhs[i]);
140662306a36Sopenharmony_ci	return err;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cierror_remove:
140962306a36Sopenharmony_ci	brelse(bh);
141062306a36Sopenharmony_ci	if (free_slots)
141162306a36Sopenharmony_ci		__fat_remove_entries(dir, pos, free_slots);
141262306a36Sopenharmony_ci	return err;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_add_entries);
1415