⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 namei_vfat.c

📁 linux环境下基于FAT的文件系统的通用代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/fs/vfat/namei.c * *  Written 1992,1993 by Werner Almesberger * *  Windows95/Windows NT compatible extended MSDOS filesystem *    by Gordon Chaffee Copyright (C) 1995.  Send bug reports for the *    VFAT filesystem to <chaffee@cs.berkeley.edu>.  Specify *    what file operation caused you trouble and if you can duplicate *    the problem, send a script that demonstrates it. * *  Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> * *  Support Multibyte characters and cleanup by *				OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> */#include <linux/module.h>#include <linux/jiffies.h>#include <linux/ctype.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/buffer_head.h>#include <linux/namei.h>#include "fat.h"/* * If new entry was created in the parent, it could create the 8.3 * alias (the shortname of logname).  So, the parent may have the * negative-dentry which matches the created 8.3 alias. * * If it happened, the negative dentry isn't actually negative * anymore.  So, drop it. */static int vfat_revalidate_shortname(struct dentry *dentry){	int ret = 1;	spin_lock(&dentry->d_lock);	if (dentry->d_time != dentry->d_parent->d_inode->i_version)		ret = 0;	spin_unlock(&dentry->d_lock);	return ret;}static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd){	/* This is not negative dentry. Always valid. */	if (dentry->d_inode)		return 1;	return vfat_revalidate_shortname(dentry);}static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd){	/*	 * This is not negative dentry. Always valid.	 *	 * Note, rename() to existing directory entry will have ->d_inode,	 * and will use existing name which isn't specified name by user.	 *	 * We may be able to drop this positive dentry here. But dropping	 * positive dentry isn't good idea. So it's unsupported like	 * rename("filename", "FILENAME") for now.	 */	if (dentry->d_inode)		return 1;	/*	 * This may be nfsd (or something), anyway, we can't see the	 * intent of this. So, since this can be for creation, drop it.	 */	if (!nd)		return 0;	/*	 * Drop the negative dentry, in order to make sure to use the	 * case sensitive name which is specified by user if this is	 * for creation.	 */	if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {		if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))			return 0;	}	return vfat_revalidate_shortname(dentry);}/* returns the length of a struct qstr, ignoring trailing dots */static unsigned int vfat_striptail_len(struct qstr *qstr){	unsigned int len = qstr->len;	while (len && qstr->name[len - 1] == '.')		len--;	return len;}/* * Compute the hash for the vfat name corresponding to the dentry. * Note: if the name is invalid, we leave the hash code unchanged so * that the existing dentry can be used. The vfat fs routines will * return ENOENT or EINVAL as appropriate. */static int vfat_hash(struct dentry *dentry, struct qstr *qstr){	qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));	return 0;}/* * Compute the hash for the vfat name corresponding to the dentry. * Note: if the name is invalid, we leave the hash code unchanged so * that the existing dentry can be used. The vfat fs routines will * return ENOENT or EINVAL as appropriate. */static int vfat_hashi(struct dentry *dentry, struct qstr *qstr){	struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;	const unsigned char *name;	unsigned int len;	unsigned long hash;	name = qstr->name;	len = vfat_striptail_len(qstr);	hash = init_name_hash();	while (len--)		hash = partial_name_hash(nls_tolower(t, *name++), hash);	qstr->hash = end_name_hash(hash);	return 0;}/* * Case insensitive compare of two vfat names. */static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b){	struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;	unsigned int alen, blen;	/* A filename cannot end in '.' or we treat it like it has none */	alen = vfat_striptail_len(a);	blen = vfat_striptail_len(b);	if (alen == blen) {		if (nls_strnicmp(t, a->name, b->name, alen) == 0)			return 0;	}	return 1;}/* * Case sensitive compare of two vfat names. */static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b){	unsigned int alen, blen;	/* A filename cannot end in '.' or we treat it like it has none */	alen = vfat_striptail_len(a);	blen = vfat_striptail_len(b);	if (alen == blen) {		if (strncmp(a->name, b->name, alen) == 0)			return 0;	}	return 1;}static struct dentry_operations vfat_ci_dentry_ops = {	.d_revalidate	= vfat_revalidate_ci,	.d_hash		= vfat_hashi,	.d_compare	= vfat_cmpi,};static struct dentry_operations vfat_dentry_ops = {	.d_revalidate	= vfat_revalidate,	.d_hash		= vfat_hash,	.d_compare	= vfat_cmp,};/* Characters that are undesirable in an MS-DOS file name */static inline wchar_t vfat_bad_char(wchar_t w){	return (w < 0x0020)	    || (w == '*') || (w == '?') || (w == '<') || (w == '>')	    || (w == '|') || (w == '"') || (w == ':') || (w == '/')	    || (w == '\\');}static inline wchar_t vfat_replace_char(wchar_t w){	return (w == '[') || (w == ']') || (w == ';') || (w == ',')	    || (w == '+') || (w == '=');}static wchar_t vfat_skip_char(wchar_t w){	return (w == '.') || (w == ' ');}static inline int vfat_is_used_badchars(const wchar_t *s, int len){	int i;	for (i = 0; i < len; i++)		if (vfat_bad_char(s[i]))			return -EINVAL;	if (s[i - 1] == ' ') /* last character cannot be space */		return -EINVAL;	return 0;}static int vfat_find_form(struct inode *dir, unsigned char *name){	struct fat_slot_info sinfo;	int err = fat_scan(dir, name, &sinfo);	if (err)		return -ENOENT;	brelse(sinfo.bh);	return 0;}/* * 1) Valid characters for the 8.3 format alias are any combination of * letters, uppercase alphabets, digits, any of the * following special characters: *     $ % ' ` - @ { } ~ ! # ( ) & _ ^ * In this case Longfilename is not stored in disk. * * WinNT's Extension: * File name and extension name is contain uppercase/lowercase * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT. * * 2) File name is 8.3 format, but it contain the uppercase and * lowercase char, muliti bytes char, etc. In this case numtail is not * added, but Longfilename is stored. * * 3) When the one except for the above, or the following special * character are contained: *        .   [ ] ; , + = * numtail is added, and Longfilename must be stored in disk . */struct shortname_info {	unsigned char lower:1,		      upper:1,		      valid:1;};#define INIT_SHORTNAME_INFO(x)	do {		\	(x)->lower = 1;				\	(x)->upper = 1;				\	(x)->valid = 1;				\} while (0)static inline int to_shortname_char(struct nls_table *nls,				    unsigned char *buf, int buf_size,				    wchar_t *src, struct shortname_info *info){	int len;	if (vfat_skip_char(*src)) {		info->valid = 0;		return 0;	}	if (vfat_replace_char(*src)) {		info->valid = 0;		buf[0] = '_';		return 1;	}	len = nls->uni2char(*src, buf, buf_size);	if (len <= 0) {		info->valid = 0;		buf[0] = '_';		len = 1;	} else if (len == 1) {		unsigned char prev = buf[0];		if (buf[0] >= 0x7F) {			info->lower = 0;			info->upper = 0;		}		buf[0] = nls_toupper(nls, buf[0]);		if (isalpha(buf[0])) {			if (buf[0] == prev)				info->lower = 0;			else				info->upper = 0;		}	} else {		info->lower = 0;		info->upper = 0;	}	return len;}/* * Given a valid longname, create a unique shortname.  Make sure the * shortname does not exist * Returns negative number on error, 0 for a normal * return, and 1 for valid shortname */static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,				 wchar_t *uname, int ulen,				 unsigned char *name_res, unsigned char *lcase){	struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options;	wchar_t *ip, *ext_start, *end, *name_start;	unsigned char base[9], ext[4], buf[8], *p;	unsigned char charbuf[NLS_MAX_CHARSET_SIZE];	int chl, chi;	int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;	int is_shortname;	struct shortname_info base_info, ext_info;	is_shortname = 1;	INIT_SHORTNAME_INFO(&base_info);	INIT_SHORTNAME_INFO(&ext_info);	/* Now, we need to create a shortname from the long name */	ext_start = end = &uname[ulen];	while (--ext_start >= uname) {		if (*ext_start == 0x002E) {	/* is `.' */			if (ext_start == end - 1) {				sz = ulen;				ext_start = NULL;			}			break;		}	}	if (ext_start == uname - 1) {		sz = ulen;		ext_start = NULL;	} else if (ext_start) {		/*		 * Names which start with a dot could be just		 * an extension eg. "...test".  In this case Win95		 * uses the extension as the name and sets no extension.		 */		name_start = &uname[0];		while (name_start < ext_start) {			if (!vfat_skip_char(*name_start))				break;			name_start++;		}		if (name_start != ext_start) {			sz = ext_start - uname;			ext_start++;		} else {			sz = ulen;			ext_start = NULL;		}	}	numtail_baselen = 6;	numtail2_baselen = 2;	for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {		chl = to_shortname_char(nls, charbuf, sizeof(charbuf),					ip, &base_info);		if (chl == 0)			continue;		if (baselen < 2 && (baselen + chl) > 2)			numtail2_baselen = baselen;		if (baselen < 6 && (baselen + chl) > 6)			numtail_baselen = baselen;		for (chi = 0; chi < chl; chi++) {			*p++ = charbuf[chi];			baselen++;			if (baselen >= 8)				break;		}		if (baselen >= 8) {			if ((chi < chl - 1) || (ip + 1) - uname < sz)				is_shortname = 0;			break;		}	}	if (baselen == 0) {		return -EINVAL;	}	extlen = 0;	if (ext_start) {		for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {			chl = to_shortname_char(nls, charbuf, sizeof(charbuf),						ip, &ext_info);			if (chl == 0)				continue;			if ((extlen + chl) > 3) {				is_shortname = 0;				break;			}			for (chi = 0; chi < chl; chi++) {				*p++ = charbuf[chi];				extlen++;			}			if (extlen >= 3) {				if (ip + 1 != end)					is_shortname = 0;				break;			}		}	}	ext[extlen] = '\0';	base[baselen] = '\0';	/* Yes, it can happen. ".\xe5" would do it. */	if (base[0] == DELETED_FLAG)		base[0] = 0x05;	/* OK, at this point we know that base is not longer than 8 symbols,	 * ext is not longer than 3, base is nonempty, both don't contain	 * any bad symbols (lowercase transformed to uppercase).	 */	memset(name_res, ' ', MSDOS_NAME);	memcpy(name_res, base, baselen);	memcpy(name_res + 8, ext, extlen);	*lcase = 0;	if (is_shortname && base_info.valid && ext_info.valid) {		if (vfat_find_form(dir, name_res) == 0)			return -EEXIST;		if (opts->shortname & VFAT_SFN_CREATE_WIN95) {			return (base_info.upper && ext_info.upper);		} else if (opts->shortname & VFAT_SFN_CREATE_WINNT) {			if ((base_info.upper || base_info.lower) &&			    (ext_info.upper || ext_info.lower)) {				if (!base_info.upper && base_info.lower)					*lcase |= CASE_LOWER_BASE;				if (!ext_info.upper && ext_info.lower)					*lcase |= CASE_LOWER_EXT;				return 1;			}			return 0;		} else {			BUG();		}	}	if (opts->numtail == 0)		if (vfat_find_form(dir, name_res) < 0)			return 0;	/*	 * Try to find a unique extension.  This used to	 * iterate through all possibilities sequentially,	 * but that gave extremely bad performance.  Windows	 * only tries a few cases before using random	 * values for part of the base.	 */	if (baselen > 6) {		baselen = numtail_baselen;		name_res[7] = ' ';	}	name_res[baselen] = '~';	for (i = 1; i < 10; i++) {		name_res[baselen + 1] = i + '0';		if (vfat_find_form(dir, name_res) < 0)			return 0;	}	i = jiffies & 0xffff;	sz = (jiffies >> 16) & 0x7;	if (baselen > 2) {		baselen = numtail2_baselen;		name_res[7] = ' ';	}	name_res[baselen + 4] = '~';	name_res[baselen + 5] = '1' + sz;	while (1) {		sprintf(buf, "%04X", i);		memcpy(&name_res[baselen], buf, 4);		if (vfat_find_form(dir, name_res) < 0)			break;		i -= 11;	}	return 0;}/* Translate a string, including coded sequences into Unicode */static intxlate_to_uni(const unsigned char *name, int len, unsigned char *outname,	     int *longlen, int *outlen, int escape, int utf8,	     struct nls_table *nls){	const unsigned char *ip;	unsigned char nc;	unsigned char *op;	unsigned int ec;	int i, k, fill;	int charlen;	if (utf8) {		int name_len = strlen(name);		*outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX);		/*		 * We stripped '.'s before and set len appropriately,		 * but utf8_mbstowcs doesn't care about len		 */		*outlen -= (name_len - len);		if (*outlen > 255)			return -ENAMETOOLONG;		op = &outname[*outlen * sizeof(wchar_t)];	} else {		if (nls) {			for (i = 0, ip = name, op = outname, *outlen = 0;			     i < len && *outlen <= 255;			     *outlen += 1)			{				if (escape && (*ip == ':')) {					if (i > len - 5)						return -EINVAL;					ec = 0;					for (k = 1; k < 5; k++) {						nc = ip[k];						ec <<= 4;						if (nc >= '0' && nc <= '9') {							ec |= nc - '0';							continue;						}						if (nc >= 'a' && nc <= 'f') {							ec |= nc - ('a' - 10);							continue;						}						if (nc >= 'A' && nc <= 'F') {							ec |= nc - ('A' - 10);							continue;						}						return -EINVAL;					}					*op++ = ec & 0xFF;					*op++ = ec >> 8;					ip += 5;					i += 5;				} else {					if ((charlen = nls->char2uni(ip, len - i, (wchar_t *)op)) < 0)						return -EINVAL;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -