📄 dir.c
字号:
/*
* linux/fs/nfs/dir.c
*
* Copyright (C) 1992 Rick Sladkey
*
* nfs directory handling functions
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/nfs_fs.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <asm/segment.h> /* for fs functions */
static int nfs_dir_read(struct inode *, struct file *filp, char *buf,
int count);
static int nfs_readdir(struct inode *, struct file *, struct dirent *, int);
static int nfs_lookup(struct inode *dir, const char *name, int len,
struct inode **result);
static int nfs_create(struct inode *dir, const char *name, int len, int mode,
struct inode **result);
static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode);
static int nfs_rmdir(struct inode *dir, const char *name, int len);
static int nfs_unlink(struct inode *dir, const char *name, int len);
static int nfs_symlink(struct inode *inode, const char *name, int len,
const char *symname);
static int nfs_link(struct inode *oldinode, struct inode *dir,
const char *name, int len);
static int nfs_mknod(struct inode *dir, const char *name, int len, int mode,
int rdev);
static int nfs_rename(struct inode *old_dir, const char *old_name,
int old_len, struct inode *new_dir, const char *new_name,
int new_len);
static struct file_operations nfs_dir_operations = {
NULL, /* lseek - default */
nfs_dir_read, /* read - bad */
NULL, /* write - bad */
nfs_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
struct inode_operations nfs_dir_inode_operations = {
&nfs_dir_operations, /* default directory file-ops */
nfs_create, /* create */
nfs_lookup, /* lookup */
nfs_link, /* link */
nfs_unlink, /* unlink */
nfs_symlink, /* symlink */
nfs_mkdir, /* mkdir */
nfs_rmdir, /* rmdir */
nfs_mknod, /* mknod */
nfs_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf,
int count)
{
return -EISDIR;
}
/*
* We need to do caching of directory entries to prevent an
* incredible amount of RPC traffic. Only the most recent open
* directory is cached. This seems sufficient for most purposes.
* Technically, we ought to flush the cache on close but this is
* not a problem in practice.
*/
static int nfs_readdir(struct inode *inode, struct file *filp,
struct dirent *dirent, int count)
{
static int c_dev = 0;
static int c_ino;
static int c_size;
static struct nfs_entry *c_entry = NULL;
int result;
int i;
struct nfs_entry *entry;
if (!inode || !S_ISDIR(inode->i_mode)) {
printk("nfs_readdir: inode is NULL or not a directory\n");
return -EBADF;
}
/* initialize cache memory if it hasn't been used before */
if (c_entry == NULL) {
i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE;
c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL);
for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
GFP_KERNEL);
}
}
entry = NULL;
/* try to find it in the cache */
if (inode->i_dev == c_dev && inode->i_ino == c_ino) {
for (i = 0; i < c_size; i++) {
if (filp->f_pos == c_entry[i].cookie) {
if (i == c_size - 1) {
if (c_entry[i].eof)
return 0;
}
else
entry = c_entry + i + 1;
break;
}
}
}
/* if we didn't find it in the cache, revert to an nfs call */
if (!entry) {
result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
filp->f_pos, NFS_READDIR_CACHE_SIZE, c_entry);
if (result < 0) {
c_dev = 0;
return result;
}
if (result > 0) {
c_dev = inode->i_dev;
c_ino = inode->i_ino;
c_size = result;
entry = c_entry + 0;
}
}
/* if we found it in the cache or from an nfs call, return results */
if (entry) {
i = strlen(entry->name);
memcpy_tofs(dirent->d_name, entry->name, i + 1);
put_fs_long(entry->fileid, &dirent->d_ino);
put_fs_word(i, &dirent->d_reclen);
filp->f_pos = entry->cookie;
return i;
}
return 0;
}
/*
* Lookup caching is a big win for performance but this is just
* a trial to see how well it works on a small scale.
* For example, bash does a lookup on ".." 13 times for each path
* element when running pwd. Yes, hard to believe but true.
* Try pwd in a filesystem mounted with noac.
*
* It trades a little cpu time and memory for a lot of network bandwidth.
* Since the cache is not hashed yet, it is a good idea not to make it too
* large because every lookup looks through the entire cache even
* though most of them will fail.
*/
static struct nfs_lookup_cache_entry {
int dev;
int inode;
char filename[NFS_MAXNAMLEN + 1];
struct nfs_fh fhandle;
struct nfs_fattr fattr;
int expiration_date;
} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
const char *filename)
{
struct nfs_lookup_cache_entry *entry;
int i;
for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
entry = nfs_lookup_cache + i;
if (entry->dev == dir->i_dev && entry->inode == dir->i_ino
&& !strncmp(filename, entry->filename, NFS_MAXNAMLEN))
return entry;
}
return NULL;
}
static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename,
struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
static int nfs_lookup_cache_in_use = 0;
struct nfs_lookup_cache_entry *entry;
if (!nfs_lookup_cache_in_use) {
memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache));
nfs_lookup_cache_in_use = 1;
}
if ((entry = nfs_lookup_cache_index(dir, filename))) {
if (jiffies > entry->expiration_date) {
entry->dev = 0;
return 0;
}
*fhandle = entry->fhandle;
*fattr = entry->fattr;
return 1;
}
return 0;
}
static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
static int nfs_lookup_cache_pos = 0;
struct nfs_lookup_cache_entry *entry;
/* compensate for bug in SGI NFS server */
if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1
|| fattr->atime.seconds == -1 || fattr->mtime.seconds == -1)
return;
if (!(entry = nfs_lookup_cache_index(dir, filename))) {
entry = nfs_lookup_cache + nfs_lookup_cache_pos++;
if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE)
nfs_lookup_cache_pos = 0;
}
entry->dev = dir->i_dev;
entry->inode = dir->i_ino;
strcpy(entry->filename, filename);
entry->fhandle = *fhandle;
entry->fattr = *fattr;
entry->expiration_date = jiffies + (S_ISDIR(fattr->mode)
? NFS_SERVER(dir)->acdirmax : NFS_SERVER(dir)->acregmax);
}
static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
const char *filename)
{
struct nfs_lookup_cache_entry *entry;
int dev;
int fileid;
int i;
if (inode) {
dev = inode->i_dev;
fileid = inode->i_ino;
}
else if ((entry = nfs_lookup_cache_index(dir, filename))) {
dev = entry->dev;
fileid = entry->fattr.fileid;
}
else
return;
for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
entry = nfs_lookup_cache + i;
if (entry->dev == dev && entry->fattr.fileid == fileid)
entry->dev = 0;
}
}
static void nfs_lookup_cache_refresh(struct inode *file,
struct nfs_fattr *fattr)
{
struct nfs_lookup_cache_entry *entry;
int dev = file->i_dev;
int fileid = file->i_ino;
int i;
for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
entry = nfs_lookup_cache + i;
if (entry->dev == dev && entry->fattr.fileid == fileid)
entry->fattr = *fattr;
}
}
static int nfs_lookup(struct inode *dir, const char *__name, int len,
struct inode **result)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
char name[len > NFS_MAXNAMLEN? 1 : len+1];
int error;
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_lookup: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -