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

📄 dir.c

📁 linux 内核源代码
💻 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/msdos_fs.h>#include <linux/dirent.h>#include <linux/smp_lock.h>#include <linux/buffer_head.h>#include <linux/compat.h>#include <asm/uaccess.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);	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",		       (unsigned long long)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, wchar_t *uni, int uni_xlate,		       struct nls_table *nls){	wchar_t *ip, ec;	unsigned char *op, nc;	int charlen;	int k;	ip = uni;	op = ascii;	while (*ip) {		ec = *ip++;		if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {			op += 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;			} else {				*op++ = '?';			}		}		/* We have some slack there, so it's OK */		if (op>ascii+256) {			op = ascii + 256;			break;		}	}	*op = 0;	return (op - ascii);}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;}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 = (wchar_t *)__get_free_page(GFP_KERNEL);		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;}/* * 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_io = sbi->nls_io;	struct nls_table *nls_disk = sbi->nls_disk;	wchar_t bufuname[14];	unsigned char xlate_len, nr_slots;	wchar_t *unicode = NULL;	unsigned char work[MSDOS_NAME], bufname[260];	/* 256 + 4 */	int uni_xlate = sbi->options.unicode_xlate;	int utf8 = sbi->options.utf8;	int anycase = (sbi->options.name_check != 's');	unsigned short opt_shortname = sbi->options.shortname;	loff_t cpos = 0;	int chl, i, j, last_u, err;	err = -ENOENT;	while(1) {		if (fat_get_entry(inode, &cpos, &bh, &de) == -1)			goto EODir;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)				return status;			else if (status == PARSE_INVALID)				continue;			else if (status == PARSE_NOT_LONGNAME)				goto parse_record;			else if (status == PARSE_EOF)				goto EODir;		}		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;		bufuname[last_u] = 0x0000;		xlate_len = utf8			?utf8_wcstombs(bufname, bufuname, sizeof(bufname))			:uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);		if (xlate_len == name_len)			if ((!anycase && !memcmp(name, bufname, xlate_len)) ||			    (anycase && !nls_strnicmp(nls_io, name, bufname,								xlate_len)))				goto Found;		if (nr_slots) {			xlate_len = utf8				?utf8_wcstombs(bufname, unicode, sizeof(bufname))				:uni16_to_x8(bufname, unicode, uni_xlate, nls_io);			if (xlate_len != name_len)				continue;			if ((!anycase && !memcmp(name, bufname, xlate_len)) ||			    (anycase && !nls_strnicmp(nls_io, name, bufname,								xlate_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;EODir:	if (unicode)		free_page((unsigned long)unicode);	return err;}EXPORT_SYMBOL_GPL(fat_search_long);struct fat_ioctl_filldir_callback {	void __user *dirent;	int result;	/* for dir ioctl */	const char *longname;	int long_len;	const char *shortname;	int short_len;};static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,			 filldir_t filldir, int short_only, int both){	struct super_block *sb = inode->i_sb;	struct msdos_sb_info *sbi = MSDOS_SB(sb);	struct buffer_head *bh;	struct msdos_dir_entry *de;	struct nls_table *nls_io = sbi->nls_io;

⌨️ 快捷键说明

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