namei.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,127 行 · 第 1/2 页

C
1,127
字号
							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;					ip += charlen;					i += charlen;					op += 2;				}			}		} else {			for (i = 0, ip = name, op = outname, *outlen = 0;			     i < len && *outlen <= 260; i++, *outlen += 1)			{				*op++ = *ip++;				*op++ = 0;			}		}	}	if (*outlen > 260)		return -ENAMETOOLONG;	*longlen = *outlen;	if (*outlen % 13) {		*op++ = 0;		*op++ = 0;		*outlen += 1;		if (*outlen % 13) {			fill = 13 - (*outlen % 13);			for (i = 0; i < fill; i++) {				*op++ = 0xff;				*op++ = 0xff;			}			*outlen += fill;		}	}	return 0;}static int vfat_build_slots(struct inode *dir, const unsigned char *name,			    int len, struct msdos_dir_slot *ds,			    int *slots, int is_dir){	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);	struct fat_mount_options *opts = &sbi->options;	struct msdos_dir_slot *ps;	struct msdos_dir_entry *de;	unsigned long page;	unsigned char cksum, lcase;	unsigned char msdos_name[MSDOS_NAME];	wchar_t *uname;	int res, slot, ulen, usize, i;	loff_t offset;	*slots = 0;	if (!vfat_valid_longname(name, len))		return -EINVAL;	if(!(page = __get_free_page(GFP_KERNEL)))		return -ENOMEM;	uname = (wchar_t *)page;	res = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize,			   opts->unicode_xlate, opts->utf8, sbi->nls_io);	if (res < 0)		goto out_free;	res = vfat_is_used_badchars(uname, ulen);	if (res < 0)		goto out_free;	res = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen,				    msdos_name, &lcase);	if (res < 0)		goto out_free;	else if (res == 1) {		de = (struct msdos_dir_entry *)ds;		res = 0;		goto shortname;	}	/* build the entry of long file name */	*slots = usize / 13;	for (cksum = i = 0; i < 11; i++) {		cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i];	}	for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {		ps->id = slot;		ps->attr = ATTR_EXT;		ps->reserved = 0;		ps->alias_checksum = cksum;		ps->start = 0;		offset = (slot - 1) * 13;		fatwchar_to16(ps->name0_4, uname + offset, 5);		fatwchar_to16(ps->name5_10, uname + offset + 5, 6);		fatwchar_to16(ps->name11_12, uname + offset + 11, 2);	}	ds[0].id |= 0x40;	de = (struct msdos_dir_entry *) ps;shortname:	/* build the entry of 8.3 alias name */	(*slots)++;	memcpy(de->name, msdos_name, MSDOS_NAME);	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;	de->lcase = lcase;	de->adate = de->cdate = de->date = 0;	de->ctime = de->time = 0;	de->ctime_ms = 0;	de->start = 0;	de->starthi = 0;	de->size = 0;out_free:	free_page(page);	return res;}static int vfat_add_entry(struct inode *dir,struct qstr* qname,			  int is_dir, struct vfat_slot_info *sinfo_out,			  struct buffer_head **bh, struct msdos_dir_entry **de){	struct msdos_dir_slot *dir_slots;	loff_t offset;	int res, slots, slot;	unsigned int len;	struct msdos_dir_entry *dummy_de;	struct buffer_head *dummy_bh;	loff_t dummy_i_pos;	len = vfat_striptail_len(qname);	if (len == 0)		return -ENOENT;	dir_slots =	       kmalloc(sizeof(struct msdos_dir_slot) * MSDOS_SLOTS, GFP_KERNEL);	if (dir_slots == NULL)		return -ENOMEM;	res = vfat_build_slots(dir, qname->name, len,			       dir_slots, &slots, is_dir);	if (res < 0)		goto cleanup;	/* build the empty directory entry of number of slots */	offset = fat_add_entries(dir, slots, &dummy_bh, &dummy_de, &dummy_i_pos);	if (offset < 0) {		res = offset;		goto cleanup;	}	brelse(dummy_bh);	/* Now create the new entry */	*bh = NULL;	for (slot = 0; slot < slots; slot++) {		if (fat_get_entry(dir, &offset, bh, de, &sinfo_out->i_pos) < 0) {			res = -EIO;			goto cleanup;		}		memcpy(*de, dir_slots + slot, sizeof(struct msdos_dir_slot));		mark_buffer_dirty(*bh);	}	res = 0;	/* update timestamp */	dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;	mark_inode_dirty(dir);	fat_date_unix2dos(dir->i_mtime.tv_sec, &(*de)->time, &(*de)->date);	(*de)->ctime = (*de)->time;	(*de)->adate = (*de)->cdate = (*de)->date;	mark_buffer_dirty(*bh);	/* slots can't be less than 1 */	sinfo_out->long_slots = slots - 1;	sinfo_out->longname_offset =		offset - sizeof(struct msdos_dir_slot) * slots;cleanup:	kfree(dir_slots);	return res;}static int vfat_find(struct inode *dir,struct qstr* qname,	struct vfat_slot_info *sinfo, struct buffer_head **last_bh,	struct msdos_dir_entry **last_de){	struct super_block *sb = dir->i_sb;	loff_t offset;	unsigned int len;	int res;	len = vfat_striptail_len(qname);	if (len == 0)		return -ENOENT;	res = fat_search_long(dir, qname->name, len,			      (MSDOS_SB(sb)->options.name_check != 's'),			      &offset, &sinfo->longname_offset);	if (res>0) {		sinfo->long_slots = res-1;		if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->i_pos)>=0)			return 0;		res = -EIO;	} 	return res ? res : -ENOENT;}static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,		struct nameidata *nd){	int res;	struct vfat_slot_info sinfo;	struct inode *inode;	struct dentry *alias;	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	int table;		lock_kernel();	table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;	dentry->d_op = &vfat_dentry_ops[table];	inode = NULL;	res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);	if (res < 0) {		table++;		goto error;	}	inode = fat_build_inode(dir->i_sb, de, sinfo.i_pos, &res);	brelse(bh);	if (res) {		unlock_kernel();		return ERR_PTR(res);	}	alias = d_find_alias(inode);	if (alias) {		if (d_invalidate(alias)==0)			dput(alias);		else {			iput(inode);			unlock_kernel();			return alias;		}			}error:	unlock_kernel();	dentry->d_op = &vfat_dentry_ops[table];	dentry->d_time = dentry->d_parent->d_inode->i_version;	dentry = d_splice_alias(inode, dentry);	if (dentry) {		dentry->d_op = &vfat_dentry_ops[table];		dentry->d_time = dentry->d_parent->d_inode->i_version;	}	return dentry;}static int vfat_create(struct inode *dir, struct dentry* dentry, int mode,		struct nameidata *nd){	struct super_block *sb = dir->i_sb;	struct inode *inode = NULL;	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	struct vfat_slot_info sinfo;	int res;	lock_kernel();	res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);	if (res < 0)		goto out;	inode = fat_build_inode(sb, de, sinfo.i_pos, &res);	brelse(bh);	if (!inode)		goto out;	res = 0;	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;	mark_inode_dirty(inode);	inode->i_version++;	dir->i_version++;	dentry->d_time = dentry->d_parent->d_inode->i_version;	d_instantiate(dentry,inode);out:	unlock_kernel();	return res;}static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,     struct buffer_head *bh, struct msdos_dir_entry *de){	loff_t offset, i_pos;	int i;	/* remove the shortname */	dir->i_mtime = dir->i_atime = CURRENT_TIME;	dir->i_version++;	mark_inode_dirty(dir);	de->name[0] = DELETED_FLAG;	mark_buffer_dirty(bh);	/* remove the longname */	offset = sinfo->longname_offset; de = NULL;	for (i = sinfo->long_slots; i > 0; --i) {		if (fat_get_entry(dir, &offset, &bh, &de, &i_pos) < 0)			continue;		de->name[0] = DELETED_FLAG;		de->attr = ATTR_NONE;		mark_buffer_dirty(bh);	}	brelse(bh);}static int vfat_rmdir(struct inode *dir, struct dentry* dentry){	int res;	struct vfat_slot_info sinfo;	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	lock_kernel();	res = fat_dir_empty(dentry->d_inode);	if (res)		goto out;	res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);	if (res < 0)		goto out;	res = 0;	dentry->d_inode->i_nlink = 0;	dentry->d_inode->i_mtime = dentry->d_inode->i_atime = CURRENT_TIME;	fat_detach(dentry->d_inode);	mark_inode_dirty(dentry->d_inode);	/* releases bh */	vfat_remove_entry(dir,&sinfo,bh,de);	dir->i_nlink--;out:	unlock_kernel();	return res;}static int vfat_unlink(struct inode *dir, struct dentry *dentry){	int res;	struct vfat_slot_info sinfo;	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	lock_kernel();	res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);	if (res < 0)		goto out;	dentry->d_inode->i_nlink = 0;	dentry->d_inode->i_mtime = dentry->d_inode->i_atime = CURRENT_TIME;	fat_detach(dentry->d_inode);	mark_inode_dirty(dentry->d_inode);	/* releases bh */	vfat_remove_entry(dir,&sinfo,bh,de);out:	unlock_kernel();	return res;}static int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode){	struct super_block *sb = dir->i_sb;	struct inode *inode = NULL;	struct vfat_slot_info sinfo;	struct buffer_head *bh = NULL;	struct msdos_dir_entry *de;	int res;	lock_kernel();	res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);	if (res < 0)		goto out;	inode = fat_build_inode(sb, de, sinfo.i_pos, &res);	if (!inode)		goto out_brelse;	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;	mark_inode_dirty(inode);	inode->i_version++;	dir->i_version++;	dir->i_nlink++;	inode->i_nlink = 2; /* no need to mark them dirty */	res = fat_new_dir(inode, dir, 1);	if (res < 0)		goto mkdir_failed;	dentry->d_time = dentry->d_parent->d_inode->i_version;	d_instantiate(dentry,inode);out_brelse:	brelse(bh);out:	unlock_kernel();	return res;mkdir_failed:	inode->i_nlink = 0;	inode->i_mtime = inode->i_atime = CURRENT_TIME;	fat_detach(inode);	mark_inode_dirty(inode);	/* releases bh */	vfat_remove_entry(dir,&sinfo,bh,de);	iput(inode);	dir->i_nlink--;	goto out;} static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,		struct inode *new_dir, struct dentry *new_dentry){	struct buffer_head *old_bh,*new_bh,*dotdot_bh;	struct msdos_dir_entry *old_de,*new_de,*dotdot_de;	loff_t dotdot_i_pos;	struct inode *old_inode, *new_inode;	int res, is_dir;	struct vfat_slot_info old_sinfo,sinfo;	old_bh = new_bh = dotdot_bh = NULL;	old_inode = old_dentry->d_inode;	new_inode = new_dentry->d_inode;	lock_kernel();	res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);	if (res < 0)		goto rename_done;	is_dir = S_ISDIR(old_inode->i_mode);	if (is_dir) {		if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,			     &dotdot_de, &dotdot_i_pos) < 0) {			res = -EIO;			goto rename_done;		}	}	if (new_dentry->d_inode) {		res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,				&new_de);		if (res < 0 || MSDOS_I(new_inode)->i_pos != sinfo.i_pos) {			/* WTF??? Cry and fail. */			printk(KERN_WARNING "vfat_rename: fs corrupted\n");			goto rename_done;		}		if (is_dir) {			res = fat_dir_empty(new_inode);			if (res)				goto rename_done;		}		fat_detach(new_inode);	} else {		res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,					&new_bh,&new_de);		if (res < 0) goto rename_done;	}	new_dir->i_version++;	/* releases old_bh */	vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);	old_bh=NULL;	fat_detach(old_inode);	fat_attach(old_inode, sinfo.i_pos);	mark_inode_dirty(old_inode);	old_dir->i_version++;	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;	mark_inode_dirty(old_dir);	if (new_inode) {		new_inode->i_nlink--;		new_inode->i_ctime=CURRENT_TIME;	}	if (is_dir) {		int start = MSDOS_I(new_dir)->i_logstart;		dotdot_de->start = cpu_to_le16(start);		dotdot_de->starthi = cpu_to_le16(start>>16);		mark_buffer_dirty(dotdot_bh);		old_dir->i_nlink--;		if (new_inode) {			new_inode->i_nlink--;		} else {			new_dir->i_nlink++;			mark_inode_dirty(new_dir);		}	}rename_done:	brelse(dotdot_bh);	brelse(old_bh);	brelse(new_bh);	unlock_kernel();	return res;}static struct inode_operations vfat_dir_inode_operations = {	.create		= vfat_create,	.lookup		= vfat_lookup,	.unlink		= vfat_unlink,	.mkdir		= vfat_mkdir,	.rmdir		= vfat_rmdir,	.rename		= vfat_rename,	.setattr	= fat_notify_change,};static int vfat_fill_super(struct super_block *sb, void *data, int silent){	int res;	res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1);	if (res)		return res;	if (MSDOS_SB(sb)->options.name_check != 's')		sb->s_root->d_op = &vfat_dentry_ops[0];	else		sb->s_root->d_op = &vfat_dentry_ops[2];	return 0;}static struct super_block *vfat_get_sb(struct file_system_type *fs_type,	int flags, const char *dev_name, void *data){	return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super);}static struct file_system_type vfat_fs_type = {	.owner		= THIS_MODULE,	.name		= "vfat",	.get_sb		= vfat_get_sb,	.kill_sb	= kill_block_super,	.fs_flags	= FS_REQUIRES_DEV,};static int __init init_vfat_fs(void){	return register_filesystem(&vfat_fs_type);}static void __exit exit_vfat_fs(void){	unregister_filesystem(&vfat_fs_type);}MODULE_LICENSE("GPL");MODULE_DESCRIPTION("VFAT filesystem support");MODULE_AUTHOR("Gordon Chaffee");module_init(init_vfat_fs)module_exit(exit_vfat_fs)

⌨️ 快捷键说明

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