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

📄 dir.c

📁 linux环境下基于FAT的文件系统的通用代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  linux/fs/fat/dir.c * *  directory handling functions for fat-based filesystems * *  Written 1992,1993 by Werner Almesberger * *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> * *  VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> *  Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> *  Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV *  Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> */#include <linux/module.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/smp_lock.h>#include <linux/buffer_head.h>#include <linux/compat.h>#include <asm/uaccess.h>#include "fat.h"static inline loff_t fat_make_i_pos(struct super_block *sb,				    struct buffer_head *bh,				    struct msdos_dir_entry *de){	return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits)		| (de - (struct msdos_dir_entry *)bh->b_data);}static inline void fat_dir_readahead(struct inode *dir, sector_t iblock,				     sector_t phys){	struct super_block *sb = dir->i_sb;	struct msdos_sb_info *sbi = MSDOS_SB(sb);	struct buffer_head *bh;	int sec;	/* This is not a first sector of cluster, or sec_per_clus == 1 */	if ((iblock & (sbi->sec_per_clus - 1)) || sbi->sec_per_clus == 1)		return;	/* root dir of FAT12/FAT16 */	if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO))		return;	bh = sb_find_get_block(sb, phys);	if (bh == NULL || !buffer_uptodate(bh)) {		for (sec = 0; sec < sbi->sec_per_clus; sec++)			sb_breadahead(sb, phys + sec);	}	brelse(bh);}/* Returns the inode number of the directory entry at offset pos. If bh is   non-NULL, it is brelse'd before. Pos is incremented. The buffer header is   returned in bh.   AV. Most often we do it item-by-item. Makes sense to optimize.   AV. OK, there we go: if both bh and de are non-NULL we assume that we just   AV. want the next entry (took one explicit de=NULL in vfat/namei.c).   AV. It's done in fat_get_entry() (inlined), here the slow case lives.   AV. Additionally, when we return -1 (i.e. reached the end of directory)   AV. we make bh NULL. */static int fat__get_entry(struct inode *dir, loff_t *pos,			  struct buffer_head **bh, struct msdos_dir_entry **de){	struct super_block *sb = dir->i_sb;	sector_t phys, iblock;	unsigned long mapped_blocks;	int err, offset;next:	if (*bh)		brelse(*bh);	*bh = NULL;	iblock = *pos >> sb->s_blocksize_bits;	err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0);	if (err || !phys)		return -1;	/* beyond EOF or error */	fat_dir_readahead(dir, iblock, phys);	*bh = sb_bread(sb, phys);	if (*bh == NULL) {		printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",		       (llu)phys);		/* skip this block */		*pos = (iblock + 1) << sb->s_blocksize_bits;		goto next;	}	offset = *pos & (sb->s_blocksize - 1);	*pos += sizeof(struct msdos_dir_entry);	*de = (struct msdos_dir_entry *)((*bh)->b_data + offset);	return 0;}static inline int fat_get_entry(struct inode *dir, loff_t *pos,				struct buffer_head **bh,				struct msdos_dir_entry **de){	/* Fast stuff first */	if (*bh && *de &&	    (*de - (struct msdos_dir_entry *)(*bh)->b_data) < MSDOS_SB(dir->i_sb)->dir_per_block - 1) {		*pos += sizeof(struct msdos_dir_entry);		(*de)++;		return 0;	}	return fat__get_entry(dir, pos, bh, de);}/* * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII. * If uni_xlate is enabled and we can't get a 1:1 conversion, use a * colon as an escape character since it is normally invalid on the vfat * filesystem. The following four characters are the hexadecimal digits * of Unicode value. This lets us do a full dump and restore of Unicode * filenames. We could get into some trouble with long Unicode names, * but ignore that right now. * Ahem... Stack smashing in ring 0 isn't fun. Fixed. */static int uni16_to_x8(unsigned char *ascii, const wchar_t *uni, int len,		       int uni_xlate, struct nls_table *nls){	const wchar_t *ip;	wchar_t ec;	unsigned char *op, nc;	int charlen;	int k;	ip = uni;	op = ascii;	while (*ip && ((len - NLS_MAX_CHARSET_SIZE) > 0)) {		ec = *ip++;		if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {			op += charlen;			len -= charlen;		} else {			if (uni_xlate == 1) {				*op = ':';				for (k = 4; k > 0; k--) {					nc = ec & 0xF;					op[k] = nc > 9	? nc + ('a' - 10)							: nc + '0';					ec >>= 4;				}				op += 5;				len -= 5;			} else {				*op++ = '?';				len--;			}		}	}	if (unlikely(*ip)) {		printk(KERN_WARNING "FAT: filename was truncated while "		       "converting.");	}	*op = 0;	return (op - ascii);}static inline int fat_uni_to_x8(struct msdos_sb_info *sbi, const wchar_t *uni,				unsigned char *buf, int size){	if (sbi->options.utf8)		return utf8_wcstombs(buf, uni, size);	else		return uni16_to_x8(buf, uni, size, sbi->options.unicode_xlate,				   sbi->nls_io);}static inline intfat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni){	int charlen;	charlen = t->char2uni(c, clen, uni);	if (charlen < 0) {		*uni = 0x003f;	/* a question mark */		charlen = 1;	}	return charlen;}static inline intfat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni){	int charlen;	wchar_t wc;	charlen = t->char2uni(c, clen, &wc);	if (charlen < 0) {		*uni = 0x003f;	/* a question mark */		charlen = 1;	} else if (charlen <= 1) {		unsigned char nc = t->charset2lower[*c];		if (!nc)			nc = *c;		if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {			*uni = 0x003f;	/* a question mark */			charlen = 1;		}	} else		*uni = wc;	return charlen;}static inline intfat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size,		  wchar_t *uni_buf, unsigned short opt, int lower){	int len = 0;	if (opt & VFAT_SFN_DISPLAY_LOWER)		len =  fat_short2lower_uni(nls, buf, buf_size, uni_buf);	else if (opt & VFAT_SFN_DISPLAY_WIN95)		len = fat_short2uni(nls, buf, buf_size, uni_buf);	else if (opt & VFAT_SFN_DISPLAY_WINNT) {		if (lower)			len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);		else			len = fat_short2uni(nls, buf, buf_size, uni_buf);	} else		len = fat_short2uni(nls, buf, buf_size, uni_buf);	return len;}static inline int fat_name_match(struct msdos_sb_info *sbi,				 const unsigned char *a, int a_len,				 const unsigned char *b, int b_len){	if (a_len != b_len)		return 0;	if (sbi->options.name_check != 's')		return !nls_strnicmp(sbi->nls_io, a, b, a_len);	else		return !memcmp(a, b, a_len);}enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, };/** * fat_parse_long - Parse extended directory entry. * * This function returns zero on success, negative value on error, or one of * the following: * * %PARSE_INVALID - Directory entry is invalid. * %PARSE_NOT_LONGNAME - Directory entry does not contain longname. * %PARSE_EOF - Directory has no more entries. */static int fat_parse_long(struct inode *dir, loff_t *pos,			  struct buffer_head **bh, struct msdos_dir_entry **de,			  wchar_t **unicode, unsigned char *nr_slots){	struct msdos_dir_slot *ds;	unsigned char id, slot, slots, alias_checksum;	if (!*unicode) {		*unicode = __getname();		if (!*unicode) {			brelse(*bh);			return -ENOMEM;		}	}parse_long:	slots = 0;	ds = (struct msdos_dir_slot *)*de;	id = ds->id;	if (!(id & 0x40))		return PARSE_INVALID;	slots = id & ~0x40;	if (slots > 20 || !slots)	/* ceil(256 * 2 / 26) */		return PARSE_INVALID;	*nr_slots = slots;	alias_checksum = ds->alias_checksum;	slot = slots;	while (1) {		int offset;		slot--;		offset = slot * 13;		fat16_towchar(*unicode + offset, ds->name0_4, 5);		fat16_towchar(*unicode + offset + 5, ds->name5_10, 6);		fat16_towchar(*unicode + offset + 11, ds->name11_12, 2);		if (ds->id & 0x40)			(*unicode)[offset + 13] = 0;		if (fat_get_entry(dir, pos, bh, de) < 0)			return PARSE_EOF;		if (slot == 0)			break;		ds = (struct msdos_dir_slot *)*de;		if (ds->attr != ATTR_EXT)			return PARSE_NOT_LONGNAME;		if ((ds->id & ~0x40) != slot)			goto parse_long;		if (ds->alias_checksum != alias_checksum)			goto parse_long;	}	if ((*de)->name[0] == DELETED_FLAG)		return PARSE_INVALID;	if ((*de)->attr == ATTR_EXT)		goto parse_long;	if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME))		return PARSE_INVALID;	if (fat_checksum((*de)->name) != alias_checksum)		*nr_slots = 0;	return 0;}/* * Maximum buffer size of short name. * [(MSDOS_NAME + '.') * max one char + nul] * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul] */#define FAT_MAX_SHORT_SIZE	((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1)/* * Maximum buffer size of unicode chars from slots. * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)] */#define FAT_MAX_UNI_CHARS	((MSDOS_SLOTS - 1) * 13 + 1)#define FAT_MAX_UNI_SIZE	(FAT_MAX_UNI_CHARS * sizeof(wchar_t))/* * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */int fat_search_long(struct inode *inode, const unsigned char *name,		    int name_len, struct fat_slot_info *sinfo){	struct super_block *sb = inode->i_sb;	struct msdos_sb_info *sbi = MSDOS_SB(sb);	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	struct nls_table *nls_disk = sbi->nls_disk;	unsigned char nr_slots;	wchar_t bufuname[14];	wchar_t *unicode = NULL;	unsigned char work[MSDOS_NAME];	unsigned char bufname[FAT_MAX_SHORT_SIZE];	unsigned short opt_shortname = sbi->options.shortname;	loff_t cpos = 0;	int chl, i, j, last_u, err, len;	err = -ENOENT;	while (1) {		if (fat_get_entry(inode, &cpos, &bh, &de) == -1)			goto end_of_dir;parse_record:		nr_slots = 0;		if (de->name[0] == DELETED_FLAG)			continue;		if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))			continue;		if (de->attr != ATTR_EXT && IS_FREE(de->name))			continue;		if (de->attr == ATTR_EXT) {			int status = fat_parse_long(inode, &cpos, &bh, &de,						    &unicode, &nr_slots);			if (status < 0) {				err = status;				goto end_of_dir;			} else if (status == PARSE_INVALID)				continue;			else if (status == PARSE_NOT_LONGNAME)				goto parse_record;			else if (status == PARSE_EOF)				goto end_of_dir;		}		memcpy(work, de->name, sizeof(de->name));		/* see namei.c, msdos_format_name */		if (work[0] == 0x05)			work[0] = 0xE5;		for (i = 0, j = 0, last_u = 0; i < 8;) {			if (!work[i])				break;			chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,						&bufuname[j++], opt_shortname,						de->lcase & CASE_LOWER_BASE);			if (chl <= 1) {				if (work[i] != ' ')					last_u = j;			} else {				last_u = j;			}			i += chl;		}		j = last_u;		fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);		for (i = 8; i < MSDOS_NAME;) {			if (!work[i])				break;			chl = fat_shortname2uni(nls_disk, &work[i],						MSDOS_NAME - i,						&bufuname[j++], opt_shortname,						de->lcase & CASE_LOWER_EXT);			if (chl <= 1) {				if (work[i] != ' ')					last_u = j;			} else {				last_u = j;			}			i += chl;		}		if (!last_u)			continue;		/* Compare shortname */		bufuname[last_u] = 0x0000;		len = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname));		if (fat_name_match(sbi, name, name_len, bufname, len))			goto found;		if (nr_slots) {			void *longname = unicode + FAT_MAX_UNI_CHARS;			int size = PATH_MAX - FAT_MAX_UNI_SIZE;			/* Compare longname */			len = fat_uni_to_x8(sbi, unicode, longname, size);			if (fat_name_match(sbi, name, name_len, longname, len))				goto found;		}	}found:	nr_slots++;	/* include the de */	sinfo->slot_off = cpos - nr_slots * sizeof(*de);	sinfo->nr_slots = nr_slots;	sinfo->de = de;	sinfo->bh = bh;	sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);	err = 0;end_of_dir:	if (unicode)		__putname(unicode);	return err;}

⌨️ 快捷键说明

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