18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/vfat/namei.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Written 1992,1993 by Werner Almesberger
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Windows95/Windows NT compatible extended MSDOS filesystem
88c2ecf20Sopenharmony_ci *    by Gordon Chaffee Copyright (C) 1995.  Send bug reports for the
98c2ecf20Sopenharmony_ci *    VFAT filesystem to <chaffee@cs.berkeley.edu>.  Specify
108c2ecf20Sopenharmony_ci *    what file operation caused you trouble and if you can duplicate
118c2ecf20Sopenharmony_ci *    the problem, send a script that demonstrates it.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *  Support Multibyte characters and cleanup by
168c2ecf20Sopenharmony_ci *				OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/ctype.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/namei.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/iversion.h>
258c2ecf20Sopenharmony_ci#include "fat.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic inline unsigned long vfat_d_version(struct dentry *dentry)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	return (unsigned long) dentry->d_fsdata;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline void vfat_d_version_set(struct dentry *dentry,
338c2ecf20Sopenharmony_ci				      unsigned long version)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	dentry->d_fsdata = (void *) version;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * If new entry was created in the parent, it could create the 8.3
408c2ecf20Sopenharmony_ci * alias (the shortname of logname).  So, the parent may have the
418c2ecf20Sopenharmony_ci * negative-dentry which matches the created 8.3 alias.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * If it happened, the negative dentry isn't actually negative
448c2ecf20Sopenharmony_ci * anymore.  So, drop it.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic int vfat_revalidate_shortname(struct dentry *dentry)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	int ret = 1;
498c2ecf20Sopenharmony_ci	spin_lock(&dentry->d_lock);
508c2ecf20Sopenharmony_ci	if (!inode_eq_iversion(d_inode(dentry->d_parent), vfat_d_version(dentry)))
518c2ecf20Sopenharmony_ci		ret = 0;
528c2ecf20Sopenharmony_ci	spin_unlock(&dentry->d_lock);
538c2ecf20Sopenharmony_ci	return ret;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int vfat_revalidate(struct dentry *dentry, unsigned int flags)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	if (flags & LOOKUP_RCU)
598c2ecf20Sopenharmony_ci		return -ECHILD;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* This is not negative dentry. Always valid. */
628c2ecf20Sopenharmony_ci	if (d_really_is_positive(dentry))
638c2ecf20Sopenharmony_ci		return 1;
648c2ecf20Sopenharmony_ci	return vfat_revalidate_shortname(dentry);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int vfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	if (flags & LOOKUP_RCU)
708c2ecf20Sopenharmony_ci		return -ECHILD;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/*
738c2ecf20Sopenharmony_ci	 * This is not negative dentry. Always valid.
748c2ecf20Sopenharmony_ci	 *
758c2ecf20Sopenharmony_ci	 * Note, rename() to existing directory entry will have ->d_inode,
768c2ecf20Sopenharmony_ci	 * and will use existing name which isn't specified name by user.
778c2ecf20Sopenharmony_ci	 *
788c2ecf20Sopenharmony_ci	 * We may be able to drop this positive dentry here. But dropping
798c2ecf20Sopenharmony_ci	 * positive dentry isn't good idea. So it's unsupported like
808c2ecf20Sopenharmony_ci	 * rename("filename", "FILENAME") for now.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	if (d_really_is_positive(dentry))
838c2ecf20Sopenharmony_ci		return 1;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * This may be nfsd (or something), anyway, we can't see the
878c2ecf20Sopenharmony_ci	 * intent of this. So, since this can be for creation, drop it.
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	if (!flags)
908c2ecf20Sopenharmony_ci		return 0;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/*
938c2ecf20Sopenharmony_ci	 * Drop the negative dentry, in order to make sure to use the
948c2ecf20Sopenharmony_ci	 * case sensitive name which is specified by user if this is
958c2ecf20Sopenharmony_ci	 * for creation.
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
988c2ecf20Sopenharmony_ci		return 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return vfat_revalidate_shortname(dentry);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* returns the length of a struct qstr, ignoring trailing dots */
1048c2ecf20Sopenharmony_cistatic unsigned int __vfat_striptail_len(unsigned int len, const char *name)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	while (len && name[len - 1] == '.')
1078c2ecf20Sopenharmony_ci		len--;
1088c2ecf20Sopenharmony_ci	return len;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic unsigned int vfat_striptail_len(const struct qstr *qstr)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return __vfat_striptail_len(qstr->len, qstr->name);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * Compute the hash for the vfat name corresponding to the dentry.
1188c2ecf20Sopenharmony_ci * Note: if the name is invalid, we leave the hash code unchanged so
1198c2ecf20Sopenharmony_ci * that the existing dentry can be used. The vfat fs routines will
1208c2ecf20Sopenharmony_ci * return ENOENT or EINVAL as appropriate.
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistatic int vfat_hash(const struct dentry *dentry, struct qstr *qstr)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	qstr->hash = full_name_hash(dentry, qstr->name, vfat_striptail_len(qstr));
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/*
1298c2ecf20Sopenharmony_ci * Compute the hash for the vfat name corresponding to the dentry.
1308c2ecf20Sopenharmony_ci * Note: if the name is invalid, we leave the hash code unchanged so
1318c2ecf20Sopenharmony_ci * that the existing dentry can be used. The vfat fs routines will
1328c2ecf20Sopenharmony_ci * return ENOENT or EINVAL as appropriate.
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_cistatic int vfat_hashi(const struct dentry *dentry, struct qstr *qstr)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
1378c2ecf20Sopenharmony_ci	const unsigned char *name;
1388c2ecf20Sopenharmony_ci	unsigned int len;
1398c2ecf20Sopenharmony_ci	unsigned long hash;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	name = qstr->name;
1428c2ecf20Sopenharmony_ci	len = vfat_striptail_len(qstr);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	hash = init_name_hash(dentry);
1458c2ecf20Sopenharmony_ci	while (len--)
1468c2ecf20Sopenharmony_ci		hash = partial_name_hash(nls_tolower(t, *name++), hash);
1478c2ecf20Sopenharmony_ci	qstr->hash = end_name_hash(hash);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * Case insensitive compare of two vfat names.
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic int vfat_cmpi(const struct dentry *dentry,
1568c2ecf20Sopenharmony_ci		unsigned int len, const char *str, const struct qstr *name)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
1598c2ecf20Sopenharmony_ci	unsigned int alen, blen;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* A filename cannot end in '.' or we treat it like it has none */
1628c2ecf20Sopenharmony_ci	alen = vfat_striptail_len(name);
1638c2ecf20Sopenharmony_ci	blen = __vfat_striptail_len(len, str);
1648c2ecf20Sopenharmony_ci	if (alen == blen) {
1658c2ecf20Sopenharmony_ci		if (nls_strnicmp(t, name->name, str, alen) == 0)
1668c2ecf20Sopenharmony_ci			return 0;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	return 1;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * Case sensitive compare of two vfat names.
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cistatic int vfat_cmp(const struct dentry *dentry,
1758c2ecf20Sopenharmony_ci		unsigned int len, const char *str, const struct qstr *name)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	unsigned int alen, blen;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* A filename cannot end in '.' or we treat it like it has none */
1808c2ecf20Sopenharmony_ci	alen = vfat_striptail_len(name);
1818c2ecf20Sopenharmony_ci	blen = __vfat_striptail_len(len, str);
1828c2ecf20Sopenharmony_ci	if (alen == blen) {
1838c2ecf20Sopenharmony_ci		if (strncmp(name->name, str, alen) == 0)
1848c2ecf20Sopenharmony_ci			return 0;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	return 1;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic const struct dentry_operations vfat_ci_dentry_ops = {
1908c2ecf20Sopenharmony_ci	.d_revalidate	= vfat_revalidate_ci,
1918c2ecf20Sopenharmony_ci	.d_hash		= vfat_hashi,
1928c2ecf20Sopenharmony_ci	.d_compare	= vfat_cmpi,
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic const struct dentry_operations vfat_dentry_ops = {
1968c2ecf20Sopenharmony_ci	.d_revalidate	= vfat_revalidate,
1978c2ecf20Sopenharmony_ci	.d_hash		= vfat_hash,
1988c2ecf20Sopenharmony_ci	.d_compare	= vfat_cmp,
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* Characters that are undesirable in an MS-DOS file name */
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic inline wchar_t vfat_bad_char(wchar_t w)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return (w < 0x0020)
2068c2ecf20Sopenharmony_ci	    || (w == '*') || (w == '?') || (w == '<') || (w == '>')
2078c2ecf20Sopenharmony_ci	    || (w == '|') || (w == '"') || (w == ':') || (w == '/')
2088c2ecf20Sopenharmony_ci	    || (w == '\\');
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic inline wchar_t vfat_replace_char(wchar_t w)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	return (w == '[') || (w == ']') || (w == ';') || (w == ',')
2148c2ecf20Sopenharmony_ci	    || (w == '+') || (w == '=');
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic wchar_t vfat_skip_char(wchar_t w)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	return (w == '.') || (w == ' ');
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic inline int vfat_is_used_badchars(const wchar_t *s, int len)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	int i;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
2278c2ecf20Sopenharmony_ci		if (vfat_bad_char(s[i]))
2288c2ecf20Sopenharmony_ci			return -EINVAL;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (s[i - 1] == ' ') /* last character cannot be space */
2318c2ecf20Sopenharmony_ci		return -EINVAL;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int vfat_find_form(struct inode *dir, unsigned char *name)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
2398c2ecf20Sopenharmony_ci	int err = fat_scan(dir, name, &sinfo);
2408c2ecf20Sopenharmony_ci	if (err)
2418c2ecf20Sopenharmony_ci		return -ENOENT;
2428c2ecf20Sopenharmony_ci	brelse(sinfo.bh);
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/*
2478c2ecf20Sopenharmony_ci * 1) Valid characters for the 8.3 format alias are any combination of
2488c2ecf20Sopenharmony_ci * letters, uppercase alphabets, digits, any of the
2498c2ecf20Sopenharmony_ci * following special characters:
2508c2ecf20Sopenharmony_ci *     $ % ' ` - @ { } ~ ! # ( ) & _ ^
2518c2ecf20Sopenharmony_ci * In this case Longfilename is not stored in disk.
2528c2ecf20Sopenharmony_ci *
2538c2ecf20Sopenharmony_ci * WinNT's Extension:
2548c2ecf20Sopenharmony_ci * File name and extension name is contain uppercase/lowercase
2558c2ecf20Sopenharmony_ci * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT.
2568c2ecf20Sopenharmony_ci *
2578c2ecf20Sopenharmony_ci * 2) File name is 8.3 format, but it contain the uppercase and
2588c2ecf20Sopenharmony_ci * lowercase char, muliti bytes char, etc. In this case numtail is not
2598c2ecf20Sopenharmony_ci * added, but Longfilename is stored.
2608c2ecf20Sopenharmony_ci *
2618c2ecf20Sopenharmony_ci * 3) When the one except for the above, or the following special
2628c2ecf20Sopenharmony_ci * character are contained:
2638c2ecf20Sopenharmony_ci *        .   [ ] ; , + =
2648c2ecf20Sopenharmony_ci * numtail is added, and Longfilename must be stored in disk .
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_cistruct shortname_info {
2678c2ecf20Sopenharmony_ci	unsigned char lower:1,
2688c2ecf20Sopenharmony_ci		      upper:1,
2698c2ecf20Sopenharmony_ci		      valid:1;
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci#define INIT_SHORTNAME_INFO(x)	do {		\
2728c2ecf20Sopenharmony_ci	(x)->lower = 1;				\
2738c2ecf20Sopenharmony_ci	(x)->upper = 1;				\
2748c2ecf20Sopenharmony_ci	(x)->valid = 1;				\
2758c2ecf20Sopenharmony_ci} while (0)
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic inline int to_shortname_char(struct nls_table *nls,
2788c2ecf20Sopenharmony_ci				    unsigned char *buf, int buf_size,
2798c2ecf20Sopenharmony_ci				    wchar_t *src, struct shortname_info *info)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int len;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (vfat_skip_char(*src)) {
2848c2ecf20Sopenharmony_ci		info->valid = 0;
2858c2ecf20Sopenharmony_ci		return 0;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	if (vfat_replace_char(*src)) {
2888c2ecf20Sopenharmony_ci		info->valid = 0;
2898c2ecf20Sopenharmony_ci		buf[0] = '_';
2908c2ecf20Sopenharmony_ci		return 1;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	len = nls->uni2char(*src, buf, buf_size);
2948c2ecf20Sopenharmony_ci	if (len <= 0) {
2958c2ecf20Sopenharmony_ci		info->valid = 0;
2968c2ecf20Sopenharmony_ci		buf[0] = '_';
2978c2ecf20Sopenharmony_ci		len = 1;
2988c2ecf20Sopenharmony_ci	} else if (len == 1) {
2998c2ecf20Sopenharmony_ci		unsigned char prev = buf[0];
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		if (buf[0] >= 0x7F) {
3028c2ecf20Sopenharmony_ci			info->lower = 0;
3038c2ecf20Sopenharmony_ci			info->upper = 0;
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		buf[0] = nls_toupper(nls, buf[0]);
3078c2ecf20Sopenharmony_ci		if (isalpha(buf[0])) {
3088c2ecf20Sopenharmony_ci			if (buf[0] == prev)
3098c2ecf20Sopenharmony_ci				info->lower = 0;
3108c2ecf20Sopenharmony_ci			else
3118c2ecf20Sopenharmony_ci				info->upper = 0;
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci	} else {
3148c2ecf20Sopenharmony_ci		info->lower = 0;
3158c2ecf20Sopenharmony_ci		info->upper = 0;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return len;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/*
3228c2ecf20Sopenharmony_ci * Given a valid longname, create a unique shortname.  Make sure the
3238c2ecf20Sopenharmony_ci * shortname does not exist
3248c2ecf20Sopenharmony_ci * Returns negative number on error, 0 for a normal
3258c2ecf20Sopenharmony_ci * return, and 1 for valid shortname
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_cistatic int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
3288c2ecf20Sopenharmony_ci				 wchar_t *uname, int ulen,
3298c2ecf20Sopenharmony_ci				 unsigned char *name_res, unsigned char *lcase)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options;
3328c2ecf20Sopenharmony_ci	wchar_t *ip, *ext_start, *end, *name_start;
3338c2ecf20Sopenharmony_ci	unsigned char base[9], ext[4], buf[5], *p;
3348c2ecf20Sopenharmony_ci	unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
3358c2ecf20Sopenharmony_ci	int chl, chi;
3368c2ecf20Sopenharmony_ci	int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
3378c2ecf20Sopenharmony_ci	int is_shortname;
3388c2ecf20Sopenharmony_ci	struct shortname_info base_info, ext_info;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	is_shortname = 1;
3418c2ecf20Sopenharmony_ci	INIT_SHORTNAME_INFO(&base_info);
3428c2ecf20Sopenharmony_ci	INIT_SHORTNAME_INFO(&ext_info);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Now, we need to create a shortname from the long name */
3458c2ecf20Sopenharmony_ci	ext_start = end = &uname[ulen];
3468c2ecf20Sopenharmony_ci	while (--ext_start >= uname) {
3478c2ecf20Sopenharmony_ci		if (*ext_start == 0x002E) {	/* is `.' */
3488c2ecf20Sopenharmony_ci			if (ext_start == end - 1) {
3498c2ecf20Sopenharmony_ci				sz = ulen;
3508c2ecf20Sopenharmony_ci				ext_start = NULL;
3518c2ecf20Sopenharmony_ci			}
3528c2ecf20Sopenharmony_ci			break;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (ext_start == uname - 1) {
3578c2ecf20Sopenharmony_ci		sz = ulen;
3588c2ecf20Sopenharmony_ci		ext_start = NULL;
3598c2ecf20Sopenharmony_ci	} else if (ext_start) {
3608c2ecf20Sopenharmony_ci		/*
3618c2ecf20Sopenharmony_ci		 * Names which start with a dot could be just
3628c2ecf20Sopenharmony_ci		 * an extension eg. "...test".  In this case Win95
3638c2ecf20Sopenharmony_ci		 * uses the extension as the name and sets no extension.
3648c2ecf20Sopenharmony_ci		 */
3658c2ecf20Sopenharmony_ci		name_start = &uname[0];
3668c2ecf20Sopenharmony_ci		while (name_start < ext_start) {
3678c2ecf20Sopenharmony_ci			if (!vfat_skip_char(*name_start))
3688c2ecf20Sopenharmony_ci				break;
3698c2ecf20Sopenharmony_ci			name_start++;
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci		if (name_start != ext_start) {
3728c2ecf20Sopenharmony_ci			sz = ext_start - uname;
3738c2ecf20Sopenharmony_ci			ext_start++;
3748c2ecf20Sopenharmony_ci		} else {
3758c2ecf20Sopenharmony_ci			sz = ulen;
3768c2ecf20Sopenharmony_ci			ext_start = NULL;
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	numtail_baselen = 6;
3818c2ecf20Sopenharmony_ci	numtail2_baselen = 2;
3828c2ecf20Sopenharmony_ci	for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {
3838c2ecf20Sopenharmony_ci		chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
3848c2ecf20Sopenharmony_ci					ip, &base_info);
3858c2ecf20Sopenharmony_ci		if (chl == 0)
3868c2ecf20Sopenharmony_ci			continue;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		if (baselen < 2 && (baselen + chl) > 2)
3898c2ecf20Sopenharmony_ci			numtail2_baselen = baselen;
3908c2ecf20Sopenharmony_ci		if (baselen < 6 && (baselen + chl) > 6)
3918c2ecf20Sopenharmony_ci			numtail_baselen = baselen;
3928c2ecf20Sopenharmony_ci		for (chi = 0; chi < chl; chi++) {
3938c2ecf20Sopenharmony_ci			*p++ = charbuf[chi];
3948c2ecf20Sopenharmony_ci			baselen++;
3958c2ecf20Sopenharmony_ci			if (baselen >= 8)
3968c2ecf20Sopenharmony_ci				break;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci		if (baselen >= 8) {
3998c2ecf20Sopenharmony_ci			if ((chi < chl - 1) || (ip + 1) - uname < sz)
4008c2ecf20Sopenharmony_ci				is_shortname = 0;
4018c2ecf20Sopenharmony_ci			break;
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	if (baselen == 0) {
4058c2ecf20Sopenharmony_ci		return -EINVAL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	extlen = 0;
4098c2ecf20Sopenharmony_ci	if (ext_start) {
4108c2ecf20Sopenharmony_ci		for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
4118c2ecf20Sopenharmony_ci			chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
4128c2ecf20Sopenharmony_ci						ip, &ext_info);
4138c2ecf20Sopenharmony_ci			if (chl == 0)
4148c2ecf20Sopenharmony_ci				continue;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci			if ((extlen + chl) > 3) {
4178c2ecf20Sopenharmony_ci				is_shortname = 0;
4188c2ecf20Sopenharmony_ci				break;
4198c2ecf20Sopenharmony_ci			}
4208c2ecf20Sopenharmony_ci			for (chi = 0; chi < chl; chi++) {
4218c2ecf20Sopenharmony_ci				*p++ = charbuf[chi];
4228c2ecf20Sopenharmony_ci				extlen++;
4238c2ecf20Sopenharmony_ci			}
4248c2ecf20Sopenharmony_ci			if (extlen >= 3) {
4258c2ecf20Sopenharmony_ci				if (ip + 1 != end)
4268c2ecf20Sopenharmony_ci					is_shortname = 0;
4278c2ecf20Sopenharmony_ci				break;
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	ext[extlen] = '\0';
4328c2ecf20Sopenharmony_ci	base[baselen] = '\0';
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Yes, it can happen. ".\xe5" would do it. */
4358c2ecf20Sopenharmony_ci	if (base[0] == DELETED_FLAG)
4368c2ecf20Sopenharmony_ci		base[0] = 0x05;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* OK, at this point we know that base is not longer than 8 symbols,
4398c2ecf20Sopenharmony_ci	 * ext is not longer than 3, base is nonempty, both don't contain
4408c2ecf20Sopenharmony_ci	 * any bad symbols (lowercase transformed to uppercase).
4418c2ecf20Sopenharmony_ci	 */
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	memset(name_res, ' ', MSDOS_NAME);
4448c2ecf20Sopenharmony_ci	memcpy(name_res, base, baselen);
4458c2ecf20Sopenharmony_ci	memcpy(name_res + 8, ext, extlen);
4468c2ecf20Sopenharmony_ci	*lcase = 0;
4478c2ecf20Sopenharmony_ci	if (is_shortname && base_info.valid && ext_info.valid) {
4488c2ecf20Sopenharmony_ci		if (vfat_find_form(dir, name_res) == 0)
4498c2ecf20Sopenharmony_ci			return -EEXIST;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		if (opts->shortname & VFAT_SFN_CREATE_WIN95) {
4528c2ecf20Sopenharmony_ci			return (base_info.upper && ext_info.upper);
4538c2ecf20Sopenharmony_ci		} else if (opts->shortname & VFAT_SFN_CREATE_WINNT) {
4548c2ecf20Sopenharmony_ci			if ((base_info.upper || base_info.lower) &&
4558c2ecf20Sopenharmony_ci			    (ext_info.upper || ext_info.lower)) {
4568c2ecf20Sopenharmony_ci				if (!base_info.upper && base_info.lower)
4578c2ecf20Sopenharmony_ci					*lcase |= CASE_LOWER_BASE;
4588c2ecf20Sopenharmony_ci				if (!ext_info.upper && ext_info.lower)
4598c2ecf20Sopenharmony_ci					*lcase |= CASE_LOWER_EXT;
4608c2ecf20Sopenharmony_ci				return 1;
4618c2ecf20Sopenharmony_ci			}
4628c2ecf20Sopenharmony_ci			return 0;
4638c2ecf20Sopenharmony_ci		} else {
4648c2ecf20Sopenharmony_ci			BUG();
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (opts->numtail == 0)
4698c2ecf20Sopenharmony_ci		if (vfat_find_form(dir, name_res) < 0)
4708c2ecf20Sopenharmony_ci			return 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/*
4738c2ecf20Sopenharmony_ci	 * Try to find a unique extension.  This used to
4748c2ecf20Sopenharmony_ci	 * iterate through all possibilities sequentially,
4758c2ecf20Sopenharmony_ci	 * but that gave extremely bad performance.  Windows
4768c2ecf20Sopenharmony_ci	 * only tries a few cases before using random
4778c2ecf20Sopenharmony_ci	 * values for part of the base.
4788c2ecf20Sopenharmony_ci	 */
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (baselen > 6) {
4818c2ecf20Sopenharmony_ci		baselen = numtail_baselen;
4828c2ecf20Sopenharmony_ci		name_res[7] = ' ';
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci	name_res[baselen] = '~';
4858c2ecf20Sopenharmony_ci	for (i = 1; i < 10; i++) {
4868c2ecf20Sopenharmony_ci		name_res[baselen + 1] = i + '0';
4878c2ecf20Sopenharmony_ci		if (vfat_find_form(dir, name_res) < 0)
4888c2ecf20Sopenharmony_ci			return 0;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	i = jiffies;
4928c2ecf20Sopenharmony_ci	sz = (jiffies >> 16) & 0x7;
4938c2ecf20Sopenharmony_ci	if (baselen > 2) {
4948c2ecf20Sopenharmony_ci		baselen = numtail2_baselen;
4958c2ecf20Sopenharmony_ci		name_res[7] = ' ';
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	name_res[baselen + 4] = '~';
4988c2ecf20Sopenharmony_ci	name_res[baselen + 5] = '1' + sz;
4998c2ecf20Sopenharmony_ci	while (1) {
5008c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "%04X", i & 0xffff);
5018c2ecf20Sopenharmony_ci		memcpy(&name_res[baselen], buf, 4);
5028c2ecf20Sopenharmony_ci		if (vfat_find_form(dir, name_res) < 0)
5038c2ecf20Sopenharmony_ci			break;
5048c2ecf20Sopenharmony_ci		i -= 11;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/* Translate a string, including coded sequences into Unicode */
5108c2ecf20Sopenharmony_cistatic int
5118c2ecf20Sopenharmony_cixlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
5128c2ecf20Sopenharmony_ci	     int *longlen, int *outlen, int escape, int utf8,
5138c2ecf20Sopenharmony_ci	     struct nls_table *nls)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	const unsigned char *ip;
5168c2ecf20Sopenharmony_ci	unsigned char *op;
5178c2ecf20Sopenharmony_ci	int i, fill;
5188c2ecf20Sopenharmony_ci	int charlen;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	if (utf8) {
5218c2ecf20Sopenharmony_ci		*outlen = utf8s_to_utf16s(name, len, UTF16_HOST_ENDIAN,
5228c2ecf20Sopenharmony_ci				(wchar_t *) outname, FAT_LFN_LEN + 2);
5238c2ecf20Sopenharmony_ci		if (*outlen < 0)
5248c2ecf20Sopenharmony_ci			return *outlen;
5258c2ecf20Sopenharmony_ci		else if (*outlen > FAT_LFN_LEN)
5268c2ecf20Sopenharmony_ci			return -ENAMETOOLONG;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		op = &outname[*outlen * sizeof(wchar_t)];
5298c2ecf20Sopenharmony_ci	} else {
5308c2ecf20Sopenharmony_ci		for (i = 0, ip = name, op = outname, *outlen = 0;
5318c2ecf20Sopenharmony_ci			 i < len && *outlen < FAT_LFN_LEN;
5328c2ecf20Sopenharmony_ci			 *outlen += 1) {
5338c2ecf20Sopenharmony_ci			if (escape && (*ip == ':')) {
5348c2ecf20Sopenharmony_ci				u8 uc[2];
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci				if (i > len - 5)
5378c2ecf20Sopenharmony_ci					return -EINVAL;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci				if (hex2bin(uc, ip + 1, 2) < 0)
5408c2ecf20Sopenharmony_ci					return -EINVAL;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci				*(wchar_t *)op = uc[0] << 8 | uc[1];
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci				op += 2;
5458c2ecf20Sopenharmony_ci				ip += 5;
5468c2ecf20Sopenharmony_ci				i += 5;
5478c2ecf20Sopenharmony_ci			} else {
5488c2ecf20Sopenharmony_ci				charlen = nls->char2uni(ip, len - i,
5498c2ecf20Sopenharmony_ci							(wchar_t *)op);
5508c2ecf20Sopenharmony_ci				if (charlen < 0)
5518c2ecf20Sopenharmony_ci					return -EINVAL;
5528c2ecf20Sopenharmony_ci				ip += charlen;
5538c2ecf20Sopenharmony_ci				i += charlen;
5548c2ecf20Sopenharmony_ci				op += 2;
5558c2ecf20Sopenharmony_ci			}
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci		if (i < len)
5588c2ecf20Sopenharmony_ci			return -ENAMETOOLONG;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	*longlen = *outlen;
5628c2ecf20Sopenharmony_ci	if (*outlen % 13) {
5638c2ecf20Sopenharmony_ci		*op++ = 0;
5648c2ecf20Sopenharmony_ci		*op++ = 0;
5658c2ecf20Sopenharmony_ci		*outlen += 1;
5668c2ecf20Sopenharmony_ci		if (*outlen % 13) {
5678c2ecf20Sopenharmony_ci			fill = 13 - (*outlen % 13);
5688c2ecf20Sopenharmony_ci			for (i = 0; i < fill; i++) {
5698c2ecf20Sopenharmony_ci				*op++ = 0xff;
5708c2ecf20Sopenharmony_ci				*op++ = 0xff;
5718c2ecf20Sopenharmony_ci			}
5728c2ecf20Sopenharmony_ci			*outlen += fill;
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int vfat_build_slots(struct inode *dir, const unsigned char *name,
5808c2ecf20Sopenharmony_ci			    int len, int is_dir, int cluster,
5818c2ecf20Sopenharmony_ci			    struct timespec64 *ts,
5828c2ecf20Sopenharmony_ci			    struct msdos_dir_slot *slots, int *nr_slots)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
5858c2ecf20Sopenharmony_ci	struct fat_mount_options *opts = &sbi->options;
5868c2ecf20Sopenharmony_ci	struct msdos_dir_slot *ps;
5878c2ecf20Sopenharmony_ci	struct msdos_dir_entry *de;
5888c2ecf20Sopenharmony_ci	unsigned char cksum, lcase;
5898c2ecf20Sopenharmony_ci	unsigned char msdos_name[MSDOS_NAME];
5908c2ecf20Sopenharmony_ci	wchar_t *uname;
5918c2ecf20Sopenharmony_ci	__le16 time, date;
5928c2ecf20Sopenharmony_ci	u8 time_cs;
5938c2ecf20Sopenharmony_ci	int err, ulen, usize, i;
5948c2ecf20Sopenharmony_ci	loff_t offset;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	*nr_slots = 0;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	uname = __getname();
5998c2ecf20Sopenharmony_ci	if (!uname)
6008c2ecf20Sopenharmony_ci		return -ENOMEM;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize,
6038c2ecf20Sopenharmony_ci			   opts->unicode_xlate, opts->utf8, sbi->nls_io);
6048c2ecf20Sopenharmony_ci	if (err)
6058c2ecf20Sopenharmony_ci		goto out_free;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	err = vfat_is_used_badchars(uname, ulen);
6088c2ecf20Sopenharmony_ci	if (err)
6098c2ecf20Sopenharmony_ci		goto out_free;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	err = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen,
6128c2ecf20Sopenharmony_ci				    msdos_name, &lcase);
6138c2ecf20Sopenharmony_ci	if (err < 0)
6148c2ecf20Sopenharmony_ci		goto out_free;
6158c2ecf20Sopenharmony_ci	else if (err == 1) {
6168c2ecf20Sopenharmony_ci		de = (struct msdos_dir_entry *)slots;
6178c2ecf20Sopenharmony_ci		err = 0;
6188c2ecf20Sopenharmony_ci		goto shortname;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* build the entry of long file name */
6228c2ecf20Sopenharmony_ci	cksum = fat_checksum(msdos_name);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	*nr_slots = usize / 13;
6258c2ecf20Sopenharmony_ci	for (ps = slots, i = *nr_slots; i > 0; i--, ps++) {
6268c2ecf20Sopenharmony_ci		ps->id = i;
6278c2ecf20Sopenharmony_ci		ps->attr = ATTR_EXT;
6288c2ecf20Sopenharmony_ci		ps->reserved = 0;
6298c2ecf20Sopenharmony_ci		ps->alias_checksum = cksum;
6308c2ecf20Sopenharmony_ci		ps->start = 0;
6318c2ecf20Sopenharmony_ci		offset = (i - 1) * 13;
6328c2ecf20Sopenharmony_ci		fatwchar_to16(ps->name0_4, uname + offset, 5);
6338c2ecf20Sopenharmony_ci		fatwchar_to16(ps->name5_10, uname + offset + 5, 6);
6348c2ecf20Sopenharmony_ci		fatwchar_to16(ps->name11_12, uname + offset + 11, 2);
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	slots[0].id |= 0x40;
6378c2ecf20Sopenharmony_ci	de = (struct msdos_dir_entry *)ps;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cishortname:
6408c2ecf20Sopenharmony_ci	/* build the entry of 8.3 alias name */
6418c2ecf20Sopenharmony_ci	(*nr_slots)++;
6428c2ecf20Sopenharmony_ci	memcpy(de->name, msdos_name, MSDOS_NAME);
6438c2ecf20Sopenharmony_ci	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
6448c2ecf20Sopenharmony_ci	de->lcase = lcase;
6458c2ecf20Sopenharmony_ci	fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
6468c2ecf20Sopenharmony_ci	de->time = de->ctime = time;
6478c2ecf20Sopenharmony_ci	de->date = de->cdate = de->adate = date;
6488c2ecf20Sopenharmony_ci	de->ctime_cs = time_cs;
6498c2ecf20Sopenharmony_ci	fat_set_start(de, cluster);
6508c2ecf20Sopenharmony_ci	de->size = 0;
6518c2ecf20Sopenharmony_ciout_free:
6528c2ecf20Sopenharmony_ci	__putname(uname);
6538c2ecf20Sopenharmony_ci	return err;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int vfat_add_entry(struct inode *dir, const struct qstr *qname,
6578c2ecf20Sopenharmony_ci			  int is_dir, int cluster, struct timespec64 *ts,
6588c2ecf20Sopenharmony_ci			  struct fat_slot_info *sinfo)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct msdos_dir_slot *slots;
6618c2ecf20Sopenharmony_ci	unsigned int len;
6628c2ecf20Sopenharmony_ci	int err, nr_slots;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	len = vfat_striptail_len(qname);
6658c2ecf20Sopenharmony_ci	if (len == 0)
6668c2ecf20Sopenharmony_ci		return -ENOENT;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	slots = kmalloc_array(MSDOS_SLOTS, sizeof(*slots), GFP_NOFS);
6698c2ecf20Sopenharmony_ci	if (slots == NULL)
6708c2ecf20Sopenharmony_ci		return -ENOMEM;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts,
6738c2ecf20Sopenharmony_ci			       slots, &nr_slots);
6748c2ecf20Sopenharmony_ci	if (err)
6758c2ecf20Sopenharmony_ci		goto cleanup;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	err = fat_add_entries(dir, slots, nr_slots, sinfo);
6788c2ecf20Sopenharmony_ci	if (err)
6798c2ecf20Sopenharmony_ci		goto cleanup;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* update timestamp */
6828c2ecf20Sopenharmony_ci	fat_truncate_time(dir, ts, S_CTIME|S_MTIME);
6838c2ecf20Sopenharmony_ci	if (IS_DIRSYNC(dir))
6848c2ecf20Sopenharmony_ci		(void)fat_sync_inode(dir);
6858c2ecf20Sopenharmony_ci	else
6868c2ecf20Sopenharmony_ci		mark_inode_dirty(dir);
6878c2ecf20Sopenharmony_cicleanup:
6888c2ecf20Sopenharmony_ci	kfree(slots);
6898c2ecf20Sopenharmony_ci	return err;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int vfat_find(struct inode *dir, const struct qstr *qname,
6938c2ecf20Sopenharmony_ci		     struct fat_slot_info *sinfo)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	unsigned int len = vfat_striptail_len(qname);
6968c2ecf20Sopenharmony_ci	if (len == 0)
6978c2ecf20Sopenharmony_ci		return -ENOENT;
6988c2ecf20Sopenharmony_ci	return fat_search_long(dir, qname->name, len, sinfo);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
7028c2ecf20Sopenharmony_ci				  unsigned int flags)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
7058c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
7068c2ecf20Sopenharmony_ci	struct inode *inode;
7078c2ecf20Sopenharmony_ci	struct dentry *alias;
7088c2ecf20Sopenharmony_ci	int err;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	err = vfat_find(dir, &dentry->d_name, &sinfo);
7138c2ecf20Sopenharmony_ci	if (err) {
7148c2ecf20Sopenharmony_ci		if (err == -ENOENT) {
7158c2ecf20Sopenharmony_ci			inode = NULL;
7168c2ecf20Sopenharmony_ci			goto out;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci		goto error;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
7228c2ecf20Sopenharmony_ci	brelse(sinfo.bh);
7238c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
7248c2ecf20Sopenharmony_ci		err = PTR_ERR(inode);
7258c2ecf20Sopenharmony_ci		goto error;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	alias = d_find_alias(inode);
7298c2ecf20Sopenharmony_ci	/*
7308c2ecf20Sopenharmony_ci	 * Checking "alias->d_parent == dentry->d_parent" to make sure
7318c2ecf20Sopenharmony_ci	 * FS is not corrupted (especially double linked dir).
7328c2ecf20Sopenharmony_ci	 */
7338c2ecf20Sopenharmony_ci	if (alias && alias->d_parent == dentry->d_parent) {
7348c2ecf20Sopenharmony_ci		/*
7358c2ecf20Sopenharmony_ci		 * This inode has non anonymous-DCACHE_DISCONNECTED
7368c2ecf20Sopenharmony_ci		 * dentry. This means, the user did ->lookup() by an
7378c2ecf20Sopenharmony_ci		 * another name (longname vs 8.3 alias of it) in past.
7388c2ecf20Sopenharmony_ci		 *
7398c2ecf20Sopenharmony_ci		 * Switch to new one for reason of locality if possible.
7408c2ecf20Sopenharmony_ci		 */
7418c2ecf20Sopenharmony_ci		if (!S_ISDIR(inode->i_mode))
7428c2ecf20Sopenharmony_ci			d_move(alias, dentry);
7438c2ecf20Sopenharmony_ci		iput(inode);
7448c2ecf20Sopenharmony_ci		mutex_unlock(&MSDOS_SB(sb)->s_lock);
7458c2ecf20Sopenharmony_ci		return alias;
7468c2ecf20Sopenharmony_ci	} else
7478c2ecf20Sopenharmony_ci		dput(alias);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ciout:
7508c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
7518c2ecf20Sopenharmony_ci	if (!inode)
7528c2ecf20Sopenharmony_ci		vfat_d_version_set(dentry, inode_query_iversion(dir));
7538c2ecf20Sopenharmony_ci	return d_splice_alias(inode, dentry);
7548c2ecf20Sopenharmony_cierror:
7558c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
7568c2ecf20Sopenharmony_ci	return ERR_PTR(err);
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
7608c2ecf20Sopenharmony_ci		       bool excl)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
7638c2ecf20Sopenharmony_ci	struct inode *inode;
7648c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
7658c2ecf20Sopenharmony_ci	struct timespec64 ts;
7668c2ecf20Sopenharmony_ci	int err;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ts = current_time(dir);
7718c2ecf20Sopenharmony_ci	err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
7728c2ecf20Sopenharmony_ci	if (err)
7738c2ecf20Sopenharmony_ci		goto out;
7748c2ecf20Sopenharmony_ci	inode_inc_iversion(dir);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
7778c2ecf20Sopenharmony_ci	brelse(sinfo.bh);
7788c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
7798c2ecf20Sopenharmony_ci		err = PTR_ERR(inode);
7808c2ecf20Sopenharmony_ci		goto out;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci	inode_inc_iversion(inode);
7838c2ecf20Sopenharmony_ci	fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
7848c2ecf20Sopenharmony_ci	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	d_instantiate(dentry, inode);
7878c2ecf20Sopenharmony_ciout:
7888c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
7898c2ecf20Sopenharmony_ci	return err;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int vfat_rmdir(struct inode *dir, struct dentry *dentry)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
7958c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
7968c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
7978c2ecf20Sopenharmony_ci	int err;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	err = fat_dir_empty(inode);
8028c2ecf20Sopenharmony_ci	if (err)
8038c2ecf20Sopenharmony_ci		goto out;
8048c2ecf20Sopenharmony_ci	err = vfat_find(dir, &dentry->d_name, &sinfo);
8058c2ecf20Sopenharmony_ci	if (err)
8068c2ecf20Sopenharmony_ci		goto out;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
8098c2ecf20Sopenharmony_ci	if (err)
8108c2ecf20Sopenharmony_ci		goto out;
8118c2ecf20Sopenharmony_ci	drop_nlink(dir);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	clear_nlink(inode);
8148c2ecf20Sopenharmony_ci	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
8158c2ecf20Sopenharmony_ci	fat_detach(inode);
8168c2ecf20Sopenharmony_ci	vfat_d_version_set(dentry, inode_query_iversion(dir));
8178c2ecf20Sopenharmony_ciout:
8188c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	return err;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int vfat_unlink(struct inode *dir, struct dentry *dentry)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
8268c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
8278c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
8288c2ecf20Sopenharmony_ci	int err;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	err = vfat_find(dir, &dentry->d_name, &sinfo);
8338c2ecf20Sopenharmony_ci	if (err)
8348c2ecf20Sopenharmony_ci		goto out;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
8378c2ecf20Sopenharmony_ci	if (err)
8388c2ecf20Sopenharmony_ci		goto out;
8398c2ecf20Sopenharmony_ci	clear_nlink(inode);
8408c2ecf20Sopenharmony_ci	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
8418c2ecf20Sopenharmony_ci	fat_detach(inode);
8428c2ecf20Sopenharmony_ci	vfat_d_version_set(dentry, inode_query_iversion(dir));
8438c2ecf20Sopenharmony_ciout:
8448c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return err;
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
8528c2ecf20Sopenharmony_ci	struct inode *inode;
8538c2ecf20Sopenharmony_ci	struct fat_slot_info sinfo;
8548c2ecf20Sopenharmony_ci	struct timespec64 ts;
8558c2ecf20Sopenharmony_ci	int err, cluster;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	ts = current_time(dir);
8608c2ecf20Sopenharmony_ci	cluster = fat_alloc_new_dir(dir, &ts);
8618c2ecf20Sopenharmony_ci	if (cluster < 0) {
8628c2ecf20Sopenharmony_ci		err = cluster;
8638c2ecf20Sopenharmony_ci		goto out;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci	err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo);
8668c2ecf20Sopenharmony_ci	if (err)
8678c2ecf20Sopenharmony_ci		goto out_free;
8688c2ecf20Sopenharmony_ci	inode_inc_iversion(dir);
8698c2ecf20Sopenharmony_ci	inc_nlink(dir);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
8728c2ecf20Sopenharmony_ci	brelse(sinfo.bh);
8738c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
8748c2ecf20Sopenharmony_ci		err = PTR_ERR(inode);
8758c2ecf20Sopenharmony_ci		/* the directory was completed, just return a error */
8768c2ecf20Sopenharmony_ci		goto out;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci	inode_inc_iversion(inode);
8798c2ecf20Sopenharmony_ci	set_nlink(inode, 2);
8808c2ecf20Sopenharmony_ci	fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
8818c2ecf20Sopenharmony_ci	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	d_instantiate(dentry, inode);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
8868c2ecf20Sopenharmony_ci	return 0;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ciout_free:
8898c2ecf20Sopenharmony_ci	fat_free_clusters(dir, cluster);
8908c2ecf20Sopenharmony_ciout:
8918c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
8928c2ecf20Sopenharmony_ci	return err;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_cistatic int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
8968c2ecf20Sopenharmony_ci		       struct inode *new_dir, struct dentry *new_dentry,
8978c2ecf20Sopenharmony_ci		       unsigned int flags)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct buffer_head *dotdot_bh;
9008c2ecf20Sopenharmony_ci	struct msdos_dir_entry *dotdot_de;
9018c2ecf20Sopenharmony_ci	struct inode *old_inode, *new_inode;
9028c2ecf20Sopenharmony_ci	struct fat_slot_info old_sinfo, sinfo;
9038c2ecf20Sopenharmony_ci	struct timespec64 ts;
9048c2ecf20Sopenharmony_ci	loff_t new_i_pos;
9058c2ecf20Sopenharmony_ci	int err, is_dir, update_dotdot, corrupt = 0;
9068c2ecf20Sopenharmony_ci	struct super_block *sb = old_dir->i_sb;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (flags & ~RENAME_NOREPLACE)
9098c2ecf20Sopenharmony_ci		return -EINVAL;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
9128c2ecf20Sopenharmony_ci	old_inode = d_inode(old_dentry);
9138c2ecf20Sopenharmony_ci	new_inode = d_inode(new_dentry);
9148c2ecf20Sopenharmony_ci	mutex_lock(&MSDOS_SB(sb)->s_lock);
9158c2ecf20Sopenharmony_ci	err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo);
9168c2ecf20Sopenharmony_ci	if (err)
9178c2ecf20Sopenharmony_ci		goto out;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	is_dir = S_ISDIR(old_inode->i_mode);
9208c2ecf20Sopenharmony_ci	update_dotdot = (is_dir && old_dir != new_dir);
9218c2ecf20Sopenharmony_ci	if (update_dotdot) {
9228c2ecf20Sopenharmony_ci		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
9238c2ecf20Sopenharmony_ci			err = -EIO;
9248c2ecf20Sopenharmony_ci			goto out;
9258c2ecf20Sopenharmony_ci		}
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	ts = current_time(old_dir);
9298c2ecf20Sopenharmony_ci	if (new_inode) {
9308c2ecf20Sopenharmony_ci		if (is_dir) {
9318c2ecf20Sopenharmony_ci			err = fat_dir_empty(new_inode);
9328c2ecf20Sopenharmony_ci			if (err)
9338c2ecf20Sopenharmony_ci				goto out;
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci		new_i_pos = MSDOS_I(new_inode)->i_pos;
9368c2ecf20Sopenharmony_ci		fat_detach(new_inode);
9378c2ecf20Sopenharmony_ci	} else {
9388c2ecf20Sopenharmony_ci		err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0,
9398c2ecf20Sopenharmony_ci				     &ts, &sinfo);
9408c2ecf20Sopenharmony_ci		if (err)
9418c2ecf20Sopenharmony_ci			goto out;
9428c2ecf20Sopenharmony_ci		new_i_pos = sinfo.i_pos;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci	inode_inc_iversion(new_dir);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	fat_detach(old_inode);
9478c2ecf20Sopenharmony_ci	fat_attach(old_inode, new_i_pos);
9488c2ecf20Sopenharmony_ci	if (IS_DIRSYNC(new_dir)) {
9498c2ecf20Sopenharmony_ci		err = fat_sync_inode(old_inode);
9508c2ecf20Sopenharmony_ci		if (err)
9518c2ecf20Sopenharmony_ci			goto error_inode;
9528c2ecf20Sopenharmony_ci	} else
9538c2ecf20Sopenharmony_ci		mark_inode_dirty(old_inode);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (update_dotdot) {
9568c2ecf20Sopenharmony_ci		fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
9578c2ecf20Sopenharmony_ci		mark_buffer_dirty_inode(dotdot_bh, old_inode);
9588c2ecf20Sopenharmony_ci		if (IS_DIRSYNC(new_dir)) {
9598c2ecf20Sopenharmony_ci			err = sync_dirty_buffer(dotdot_bh);
9608c2ecf20Sopenharmony_ci			if (err)
9618c2ecf20Sopenharmony_ci				goto error_dotdot;
9628c2ecf20Sopenharmony_ci		}
9638c2ecf20Sopenharmony_ci		drop_nlink(old_dir);
9648c2ecf20Sopenharmony_ci		if (!new_inode)
9658c2ecf20Sopenharmony_ci 			inc_nlink(new_dir);
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
9698c2ecf20Sopenharmony_ci	old_sinfo.bh = NULL;
9708c2ecf20Sopenharmony_ci	if (err)
9718c2ecf20Sopenharmony_ci		goto error_dotdot;
9728c2ecf20Sopenharmony_ci	inode_inc_iversion(old_dir);
9738c2ecf20Sopenharmony_ci	fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME);
9748c2ecf20Sopenharmony_ci	if (IS_DIRSYNC(old_dir))
9758c2ecf20Sopenharmony_ci		(void)fat_sync_inode(old_dir);
9768c2ecf20Sopenharmony_ci	else
9778c2ecf20Sopenharmony_ci		mark_inode_dirty(old_dir);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (new_inode) {
9808c2ecf20Sopenharmony_ci		drop_nlink(new_inode);
9818c2ecf20Sopenharmony_ci		if (is_dir)
9828c2ecf20Sopenharmony_ci			drop_nlink(new_inode);
9838c2ecf20Sopenharmony_ci		fat_truncate_time(new_inode, &ts, S_CTIME);
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ciout:
9868c2ecf20Sopenharmony_ci	brelse(sinfo.bh);
9878c2ecf20Sopenharmony_ci	brelse(dotdot_bh);
9888c2ecf20Sopenharmony_ci	brelse(old_sinfo.bh);
9898c2ecf20Sopenharmony_ci	mutex_unlock(&MSDOS_SB(sb)->s_lock);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	return err;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cierror_dotdot:
9948c2ecf20Sopenharmony_ci	/* data cluster is shared, serious corruption */
9958c2ecf20Sopenharmony_ci	corrupt = 1;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	if (update_dotdot) {
9988c2ecf20Sopenharmony_ci		fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
9998c2ecf20Sopenharmony_ci		mark_buffer_dirty_inode(dotdot_bh, old_inode);
10008c2ecf20Sopenharmony_ci		corrupt |= sync_dirty_buffer(dotdot_bh);
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_cierror_inode:
10038c2ecf20Sopenharmony_ci	fat_detach(old_inode);
10048c2ecf20Sopenharmony_ci	fat_attach(old_inode, old_sinfo.i_pos);
10058c2ecf20Sopenharmony_ci	if (new_inode) {
10068c2ecf20Sopenharmony_ci		fat_attach(new_inode, new_i_pos);
10078c2ecf20Sopenharmony_ci		if (corrupt)
10088c2ecf20Sopenharmony_ci			corrupt |= fat_sync_inode(new_inode);
10098c2ecf20Sopenharmony_ci	} else {
10108c2ecf20Sopenharmony_ci		/*
10118c2ecf20Sopenharmony_ci		 * If new entry was not sharing the data cluster, it
10128c2ecf20Sopenharmony_ci		 * shouldn't be serious corruption.
10138c2ecf20Sopenharmony_ci		 */
10148c2ecf20Sopenharmony_ci		int err2 = fat_remove_entries(new_dir, &sinfo);
10158c2ecf20Sopenharmony_ci		if (corrupt)
10168c2ecf20Sopenharmony_ci			corrupt |= err2;
10178c2ecf20Sopenharmony_ci		sinfo.bh = NULL;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci	if (corrupt < 0) {
10208c2ecf20Sopenharmony_ci		fat_fs_error(new_dir->i_sb,
10218c2ecf20Sopenharmony_ci			     "%s: Filesystem corrupted (i_pos %lld)",
10228c2ecf20Sopenharmony_ci			     __func__, sinfo.i_pos);
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci	goto out;
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_cistatic const struct inode_operations vfat_dir_inode_operations = {
10288c2ecf20Sopenharmony_ci	.create		= vfat_create,
10298c2ecf20Sopenharmony_ci	.lookup		= vfat_lookup,
10308c2ecf20Sopenharmony_ci	.unlink		= vfat_unlink,
10318c2ecf20Sopenharmony_ci	.mkdir		= vfat_mkdir,
10328c2ecf20Sopenharmony_ci	.rmdir		= vfat_rmdir,
10338c2ecf20Sopenharmony_ci	.rename		= vfat_rename,
10348c2ecf20Sopenharmony_ci	.setattr	= fat_setattr,
10358c2ecf20Sopenharmony_ci	.getattr	= fat_getattr,
10368c2ecf20Sopenharmony_ci	.update_time	= fat_update_time,
10378c2ecf20Sopenharmony_ci};
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic void setup(struct super_block *sb)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	MSDOS_SB(sb)->dir_ops = &vfat_dir_inode_operations;
10428c2ecf20Sopenharmony_ci	if (MSDOS_SB(sb)->options.name_check != 's')
10438c2ecf20Sopenharmony_ci		sb->s_d_op = &vfat_ci_dentry_ops;
10448c2ecf20Sopenharmony_ci	else
10458c2ecf20Sopenharmony_ci		sb->s_d_op = &vfat_dentry_ops;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic int vfat_fill_super(struct super_block *sb, void *data, int silent)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	return fat_fill_super(sb, data, silent, 1, setup);
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic struct dentry *vfat_mount(struct file_system_type *fs_type,
10548c2ecf20Sopenharmony_ci		       int flags, const char *dev_name,
10558c2ecf20Sopenharmony_ci		       void *data)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, vfat_fill_super);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic struct file_system_type vfat_fs_type = {
10618c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
10628c2ecf20Sopenharmony_ci	.name		= "vfat",
10638c2ecf20Sopenharmony_ci	.mount		= vfat_mount,
10648c2ecf20Sopenharmony_ci	.kill_sb	= kill_block_super,
10658c2ecf20Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
10668c2ecf20Sopenharmony_ci};
10678c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("vfat");
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_cistatic int __init init_vfat_fs(void)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	return register_filesystem(&vfat_fs_type);
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic void __exit exit_vfat_fs(void)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	unregister_filesystem(&vfat_fs_type);
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VFAT filesystem support");
10818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gordon Chaffee");
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cimodule_init(init_vfat_fs)
10848c2ecf20Sopenharmony_cimodule_exit(exit_vfat_fs)
1085