dir.c

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

C
667
字号
/* dir.c: AFS filesystem directory handling * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/smp_lock.h>#include "vnode.h"#include "volume.h"#include <rxrpc/call.h>#include "super.h"#include "internal.h"static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,				     struct nameidata *nd);static int afs_dir_open(struct inode *inode, struct file *file);static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir);static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);static int afs_d_delete(struct dentry *dentry);static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen,				  loff_t fpos, ino_t ino, unsigned dtype);struct file_operations afs_dir_file_operations = {	.open		= afs_dir_open,	.readdir	= afs_dir_readdir,};struct inode_operations afs_dir_inode_operations = {	.lookup		= afs_dir_lookup,	.getattr	= afs_inode_getattr,#if 0 /* TODO */	.create		= afs_dir_create,	.link		= afs_dir_link,	.unlink		= afs_dir_unlink,	.symlink	= afs_dir_symlink,	.mkdir		= afs_dir_mkdir,	.rmdir		= afs_dir_rmdir,	.mknod		= afs_dir_mknod,	.rename		= afs_dir_rename,#endif};static struct dentry_operations afs_fs_dentry_operations = {	.d_revalidate	= afs_d_revalidate,	.d_delete	= afs_d_delete,};#define AFS_DIR_HASHTBL_SIZE	128#define AFS_DIR_DIRENT_SIZE	32#define AFS_DIRENT_PER_BLOCK	64union afs_dirent {	struct {		uint8_t		valid;		uint8_t		unused[1];		__be16		hash_next;		__be32		vnode;		__be32		unique;		uint8_t		name[16];		uint8_t		overflow[4];	/* if any char of the name (inc						 * NUL) reaches here, consume						 * the next dirent too */	} u;	uint8_t	extended_name[32];};/* AFS directory page header (one at the beginning of every 2048-byte chunk) */struct afs_dir_pagehdr {	__be16		npages;	__be16		magic;#define AFS_DIR_MAGIC htons(1234)	uint8_t		nentries;	uint8_t		bitmap[8];	uint8_t		pad[19];};/* directory block layout */union afs_dir_block {	struct afs_dir_pagehdr pagehdr;	struct {		struct afs_dir_pagehdr	pagehdr;		uint8_t			alloc_ctrs[128];		/* dir hash table */		uint16_t		hashtable[AFS_DIR_HASHTBL_SIZE];	} hdr;	union afs_dirent dirents[AFS_DIRENT_PER_BLOCK];};/* layout on a linux VM page */struct afs_dir_page {	union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)];};struct afs_dir_lookup_cookie {	struct afs_fid	fid;	const char	*name;	size_t		nlen;	int		found;};/*****************************************************************************//* * check that a directory page is valid */static inline void afs_dir_check_page(struct inode *dir, struct page *page){	struct afs_dir_page *dbuf;	loff_t latter;	int tmp, qty;#if 0	/* check the page count */	qty = desc.size / sizeof(dbuf->blocks[0]);	if (qty == 0)		goto error;	if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) {		printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",		       __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages));		goto error;	}#endif	/* determine how many magic numbers there should be in this page */	latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT);	if (latter >= PAGE_SIZE)		qty = PAGE_SIZE;	else		qty = latter;	qty /= sizeof(union afs_dir_block);	/* check them */	dbuf = page_address(page);	for (tmp = 0; tmp < qty; tmp++) {		if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) {			printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n",			       __FUNCTION__, dir->i_ino, tmp, qty,			       ntohs(dbuf->blocks[tmp].pagehdr.magic));			goto error;		}	}	SetPageChecked(page);	return; error:	SetPageChecked(page);	SetPageError(page);} /* end afs_dir_check_page() *//*****************************************************************************//* * discard a page cached in the pagecache */static inline void afs_dir_put_page(struct page *page){	kunmap(page);	page_cache_release(page);} /* end afs_dir_put_page() *//*****************************************************************************//* * get a page into the pagecache */static struct page *afs_dir_get_page(struct inode *dir, unsigned long index){	struct page *page;	_enter("{%lu},%lu", dir->i_ino, index);	page = read_cache_page(dir->i_mapping,index,			       (filler_t *) dir->i_mapping->a_ops->readpage,			       NULL);	if (!IS_ERR(page)) {		wait_on_page_locked(page);		kmap(page);		if (!PageUptodate(page))			goto fail;		if (!PageChecked(page))			afs_dir_check_page(dir, page);		if (PageError(page))			goto fail;	}	return page; fail:	afs_dir_put_page(page);	return ERR_PTR(-EIO);} /* end afs_dir_get_page() *//*****************************************************************************//* * open an AFS directory file */static int afs_dir_open(struct inode *inode, struct file *file){	_enter("{%lu}", inode->i_ino);	BUG_ON(sizeof(union afs_dir_block) != 2048);	BUG_ON(sizeof(union afs_dirent) != 32);	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED)		return -ENOENT;	_leave(" = 0");	return 0;} /* end afs_dir_open() *//*****************************************************************************//* * deal with one block in an AFS directory */static int afs_dir_iterate_block(unsigned *fpos,				 union afs_dir_block *block,				 unsigned blkoff,				 void *cookie,				 filldir_t filldir){	union afs_dirent *dire;	unsigned offset, next, curr;	size_t nlen;	int tmp, ret;	_enter("%u,%x,%p,,",*fpos,blkoff,block);	curr = (*fpos - blkoff) / sizeof(union afs_dirent);	/* walk through the block, an entry at a time */	for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries;	     offset < AFS_DIRENT_PER_BLOCK;	     offset = next	     ) {		next = offset + 1;		/* skip entries marked unused in the bitmap */		if (!(block->pagehdr.bitmap[offset / 8] &		      (1 << (offset % 8)))) {			_debug("ENT[%Zu.%u]: unused\n",			       blkoff / sizeof(union afs_dir_block), offset);			if (offset >= curr)				*fpos = blkoff +					next * sizeof(union afs_dirent);			continue;		}		/* got a valid entry */		dire = &block->dirents[offset];		nlen = strnlen(dire->u.name,			       sizeof(*block) -			       offset * sizeof(union afs_dirent));		_debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n",		       blkoff / sizeof(union afs_dir_block), offset,		       (offset < curr ? "skip" : "fill"),		       nlen, dire->u.name);		/* work out where the next possible entry is */		for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) {			if (next >= AFS_DIRENT_PER_BLOCK) {				_debug("ENT[%Zu.%u]:"				       " %u travelled beyond end dir block"				       " (len %u/%Zu)\n",				       blkoff / sizeof(union afs_dir_block),				       offset, next, tmp, nlen);				return -EIO;			}			if (!(block->pagehdr.bitmap[next / 8] &			      (1 << (next % 8)))) {				_debug("ENT[%Zu.%u]:"				       " %u unmarked extension (len %u/%Zu)\n",				       blkoff / sizeof(union afs_dir_block),				       offset, next, tmp, nlen);				return -EIO;			}			_debug("ENT[%Zu.%u]: ext %u/%Zu\n",			       blkoff / sizeof(union afs_dir_block),			       next, tmp, nlen);			next++;		}		/* skip if starts before the current position */		if (offset < curr)			continue;		/* found the next entry */		ret = filldir(cookie,			      dire->u.name,			      nlen,			      blkoff + offset * sizeof(union afs_dirent),			      ntohl(dire->u.vnode),			      filldir == afs_dir_lookup_filldir ?			      ntohl(dire->u.unique) : DT_UNKNOWN);		if (ret < 0) {			_leave(" = 0 [full]");			return 0;		}		*fpos = blkoff + next * sizeof(union afs_dirent);	}	_leave(" = 1 [more]");	return 1;} /* end afs_dir_iterate_block() *//*****************************************************************************//* * read an AFS directory */static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie,			   filldir_t filldir){	union afs_dir_block	*dblock;	struct afs_dir_page *dbuf;	struct page *page;	unsigned blkoff, limit;

⌨️ 快捷键说明

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