OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/fs/nfs
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/dir.c
0,0 → 1,759
/*
* linux/fs/nfs/dir.c
*
* Copyright (C) 1992 Rick Sladkey
*
* nfs directory handling functions
*
* 10 Apr 1996 Added silly rename for unlink --okir
*/
 
/*
* Fixes:
* Ion Badulescu <ionut@cs.columbia.edu> : FIFO's need special handling in NFSv2
*/
 
#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_open(struct inode * inode, struct file * file);
static int nfs_dir_read(struct inode *, struct file *, char *, int);
static int nfs_readdir(struct inode *, struct file *, void *, filldir_t);
static int nfs_lookup(struct inode *, const char *, int, struct inode **);
static int nfs_create(struct inode *, const char *, int, int, struct inode **);
static int nfs_mkdir(struct inode *, const char *, int, int);
static int nfs_rmdir(struct inode *, const char *, int);
static int nfs_unlink(struct inode *, const char *, int);
static int nfs_symlink(struct inode *, const char *, int, const char *);
static int nfs_link(struct inode *, struct inode *, const char *, int);
static int nfs_mknod(struct inode *, const char *, int, int, int);
static int nfs_rename(struct inode *, const char *, int,
struct inode *, const char *, int, int);
 
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 */
nfs_dir_open, /* open - revalidate */
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, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
 
static inline void revalidate_dir(struct nfs_server * server, struct inode * dir)
{
struct nfs_fattr fattr;
 
if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir))
return;
 
NFS_READTIME(dir) = jiffies;
if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) {
nfs_refresh_inode(dir, &fattr);
if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) {
if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax)
NFS_ATTRTIMEO(dir) = server->acdirmax;
return;
}
NFS_OLDMTIME(dir) = fattr.mtime.seconds;
}
/* invalidate directory cache here when we _really_ start caching */
}
 
static int nfs_dir_open(struct inode * dir, struct file * file)
{
revalidate_dir(NFS_SERVER(dir), dir);
return 0;
}
 
static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf,
int count)
{
return -EISDIR;
}
 
static struct nfs_entry *c_entry = NULL;
 
/*
* 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,
void *dirent, filldir_t filldir)
{
static kdev_t c_dev = 0;
static int c_ino;
static int c_size;
 
int result;
int i, index = 0;
struct nfs_entry *entry;
 
if (!inode || !S_ISDIR(inode->i_mode)) {
printk("nfs_readdir: inode is NULL or not a directory\n");
return -EBADF;
}
 
revalidate_dir(NFS_SERVER(inode), inode);
 
/* 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);
if (c_entry == NULL) {
printk("nfs_readdir: no MEMORY for cache\n");
return -ENOMEM;
}
for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
GFP_KERNEL);
if (c_entry[i].name == NULL) {
printk("nfs_readdir: no MEMORY for cache\n");
while (--i>=0)
kfree(c_entry[i].name);
kfree(c_entry);
c_entry = NULL;
return -ENOMEM;
}
}
}
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 + (index = 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 + (index = 0);
}
}
 
/* if we found it in the cache or from an nfs call, return results */
if (!entry)
return 0;
while (index < c_size) {
int nextpos = entry->cookie;
if (filldir(dirent, entry->name, strlen(entry->name), filp->f_pos, entry->fileid) < 0)
break;
filp->f_pos = nextpos;
/* revalidate the cache if we slept in filldir() */
if (inode->i_dev != c_dev)
break;
if (inode->i_ino != c_ino)
break;
if (nextpos != entry->cookie)
break;
index++;
entry++;
}
return 0;
}
 
/*
* free cache memory
* called from cleanup_module
*/
 
void nfs_kfree_cache(void)
{
int i;
 
if (c_entry == NULL)
return;
for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++)
kfree(c_entry[i].name);
kfree(c_entry);
c_entry = NULL;
}
 
/*
* 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 {
kdev_t 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)->acdirmin : NFS_SERVER(dir)->acregmin);
}
 
static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
const char *filename)
{
struct nfs_lookup_cache_entry *entry;
kdev_t 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;
kdev_t 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;
}
memcpy(name,__name,len);
name[len] = '\0';
if (len == 1 && name[0] == '.') { /* cheat for "." */
*result = dir;
return 0;
}
if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC)
|| !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) {
if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
name, &fhandle, &fattr))) {
iput(dir);
return error;
}
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
}
if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) {
iput(dir);
return -EACCES;
}
iput(dir);
return 0;
}
 
static int nfs_create(struct inode *dir, const char *name, int len, int mode,
struct inode **result)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
int error;
 
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_create: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr))) {
iput(dir);
return error;
}
if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) {
iput(dir);
return -EACCES;
}
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
iput(dir);
return 0;
}
 
static int nfs_mknod(struct inode *dir, const char *name, int len,
int mode, int rdev)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
int error;
 
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mknod: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
if (S_ISFIFO(mode))
sattr.mode = (mode & ~S_IFMT) | S_IFCHR;
else
sattr.mode = mode;
sattr.uid = sattr.gid = (unsigned) -1;
if (S_ISCHR(mode) || S_ISBLK(mode))
sattr.size = rdev; /* get out your barf bag */
else
sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
if (error == -EINVAL && (S_ISFIFO(mode))) {
sattr.mode = mode;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
}
if (!error)
{
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
/* The parent dir inode count may have changed ! */
nfs_lookup_cache_remove( NULL, dir, NULL);
}
iput(dir);
return error;
}
 
static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
int error;
 
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mkdir: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
if (!error) {
if (fattr.fileid == dir->i_ino)
printk("Sony NewsOS 4.1R buggy nfs server?\n");
else
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
}
iput(dir);
/* The parent dir link count may have changed */
nfs_lookup_cache_remove( NULL, dir, NULL);
return error;
}
 
static int nfs_rmdir(struct inode *dir, const char *name, int len)
{
int error;
 
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_rmdir: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
nfs_lookup_cache_remove(dir, NULL, name);
iput(dir);
return error;
}
 
static int nfs_sillyrename(struct inode *dir, const char *name, int len)
{
struct inode *inode;
char silly[16];
int slen, ret;
 
dir->i_count++;
if (nfs_lookup(dir, name, len, &inode) < 0)
return -EIO; /* arbitrary */
if (inode->i_count == 1 || NFS_RENAMED_DIR(inode)) {
iput(inode);
return -EIO;
}
slen = sprintf(silly, ".nfs%ld", inode->i_ino);
 
if (len == slen && !strncmp(name, silly, len)) {
iput(inode);
return -EIO; /* DWIM */
}
ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name,
NFS_FH(dir), silly, 0);
if (ret >= 0) {
nfs_lookup_cache_remove(dir, NULL, name);
nfs_lookup_cache_remove(dir, NULL, silly);
NFS_RENAMED_DIR(inode) = dir;
dir->i_count++;
}
iput(inode);
return ret;
}
 
void nfs_sillyrename_cleanup(struct inode *inode)
{
struct inode *dir = NFS_RENAMED_DIR(inode);
char silly[14];
int error, slen;
 
slen = sprintf(silly, ".nfs%ld", inode->i_ino);
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
nfs_lookup_cache_remove(dir, NULL, silly);
if (error < 0)
printk("NFS silly_rename cleanup failed (err = %d)\n",
-error);
NFS_RENAMED_DIR(inode) = NULL;
iput(dir);
}
 
static int nfs_unlink(struct inode *dir, const char *name, int len)
{
int error;
 
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_unlink: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
if ((error = nfs_sillyrename(dir, name, len)) < 0) {
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
nfs_lookup_cache_remove(dir, NULL, name);
}
iput(dir);
return error;
}
 
static int nfs_symlink(struct inode *dir, const char *name, int len,
const char *symname)
{
struct nfs_sattr sattr;
int error;
 
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_symlink: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(dir);
return -ENAMETOOLONG;
}
if (strlen(symname) > NFS_MAXPATHLEN) {
iput(dir);
return -ENAMETOOLONG;
}
sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
name, symname, &sattr);
iput(dir);
return error;
}
 
static int nfs_link(struct inode *oldinode, struct inode *dir,
const char *name, int len)
{
int error;
 
if (!oldinode) {
printk("nfs_link: old inode is NULL\n");
iput(oldinode);
iput(dir);
return -ENOENT;
}
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_link: dir is NULL or not a directory\n");
iput(oldinode);
iput(dir);
return -ENOENT;
}
if (len > NFS_MAXNAMLEN) {
iput(oldinode);
iput(dir);
return -ENAMETOOLONG;
}
error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
NFS_FH(dir), name);
 
nfs_lookup_cache_remove(dir, oldinode, NULL);
NFS_CACHEINV(oldinode);
iput(oldinode);
iput(dir);
return error;
}
 
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,
int must_be_dir)
{
int error;
 
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
printk("nfs_rename: old inode is NULL or not a directory\n");
iput(old_dir);
iput(new_dir);
return -ENOENT;
}
if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
printk("nfs_rename: new inode is NULL or not a directory\n");
iput(old_dir);
iput(new_dir);
return -ENOENT;
}
if (old_len > NFS_MAXNAMLEN || new_len > NFS_MAXNAMLEN) {
iput(old_dir);
iput(new_dir);
return -ENAMETOOLONG;
}
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_name,
NFS_FH(new_dir), new_name,
must_be_dir);
 
nfs_lookup_cache_remove(old_dir, NULL, old_name);
nfs_lookup_cache_remove(new_dir, NULL, new_name);
iput(old_dir);
iput(new_dir);
return error;
}
 
/*
* Many nfs protocol calls return the new file attributes after
* an operation. Here we update the inode to reflect the state
* of the server's inode.
*/
 
void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
int was_empty;
 
if (!inode || !fattr) {
printk("nfs_refresh_inode: inode or fattr is NULL\n");
return;
}
if (inode->i_ino != fattr->fileid) {
printk("nfs_refresh_inode: inode number mismatch\n");
return;
}
was_empty = (inode->i_mode == 0);
/*
* Some (broken?) NFS servers return 0 as the file type.
* We'll assume that 0 means the file type has not changed.
*/
if(!(fattr->mode & S_IFMT)){
inode->i_mode = (inode->i_mode & S_IFMT) |
(fattr->mode & ~S_IFMT);
}else{
inode->i_mode = fattr->mode;
}
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
inode->i_gid = fattr->gid;
 
/* Size changed from outside: invalidate caches on next read */
if (inode->i_size != fattr->size)
NFS_CACHEINV(inode);
if (NFS_OLDMTIME(inode) != fattr->mtime.seconds)
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
inode->i_size = fattr->size;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
inode->i_rdev = to_kdev_t(fattr->rdev);
else
inode->i_rdev = 0;
inode->i_blocks = fattr->blocks;
inode->i_atime = fattr->atime.seconds;
inode->i_mtime = fattr->mtime.seconds;
inode->i_ctime = fattr->ctime.seconds;
if (S_ISREG(inode->i_mode))
inode->i_op = &nfs_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &nfs_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &nfs_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
else if (S_ISFIFO(inode->i_mode)) {
if (was_empty)
init_fifo(inode);
} else
inode->i_op = NULL;
nfs_lookup_cache_refresh(inode, fattr);
}
 
/inode.c
0,0 → 1,381
/*
* linux/fs/nfs/inode.c
*
* Copyright (C) 1992 Rick Sladkey
*
* nfs inode and superblock handling functions
*
* Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
* experimental NFS changes. Modularisation taken straight from SYS5 fs.
*
* Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
* J.S.Peatfield@damtp.cam.ac.uk
*
*/
 
#include <linux/module.h>
 
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/nfsiod.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
 
#include <asm/system.h>
#include <asm/segment.h>
 
/* This is for kernel_thread */
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
 
extern int close_fp(struct file *filp);
 
static int nfs_notify_change(struct inode *, struct iattr *);
static void nfs_put_inode(struct inode *);
static void nfs_put_super(struct super_block *);
static void nfs_read_inode(struct inode *);
static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
 
static struct super_operations nfs_sops = {
nfs_read_inode, /* read inode */
nfs_notify_change, /* notify change */
NULL, /* write inode */
nfs_put_inode, /* put inode */
nfs_put_super, /* put superblock */
NULL, /* write superblock */
nfs_statfs, /* stat filesystem */
NULL
};
 
/*
* The "read_inode" function doesn't actually do anything:
* the real data is filled in later in nfs_fhget. Here we
* just mark the cache times invalid, and zero out i_mode
* (the latter makes "nfs_refresh_inode" do the right thing
* wrt pipe inodes)
*/
static void nfs_read_inode(struct inode * inode)
{
int rsize = inode->i_sb->u.nfs_sb.s_server.rsize;
int size = inode->i_sb->u.nfs_sb.s_server.wsize;
 
if (rsize > size)
size = rsize;
inode->i_blksize = size;
inode->i_mode = 0;
inode->i_op = NULL;
NFS_CACHEINV(inode);
}
 
static void nfs_put_inode(struct inode * inode)
{
if (NFS_RENAMED_DIR(inode))
nfs_sillyrename_cleanup(inode);
if (inode->i_pipe)
clear_inode(inode);
}
 
void nfs_put_super(struct super_block *sb)
{
close_fp(sb->u.nfs_sb.s_server.file);
rpc_closesock(sb->u.nfs_sb.s_server.rsock);
lock_super(sb);
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
}
 
/*
* The way this works is that the mount process passes a structure
* in the data argument which contains an open socket to the NFS
* server and the root file handle obtained from the server's mount
* daemon. We stash these away in the private superblock fields.
* Later we can add other mount parameters like caching values.
*/
 
struct super_block *nfs_read_super(struct super_block *sb, void *raw_data,
int silent)
{
struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
struct nfs_server *server;
unsigned int fd;
struct file *filp;
 
kdev_t dev = sb->s_dev;
 
MOD_INC_USE_COUNT;
if (!data) {
printk("nfs_read_super: missing data argument\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
fd = data->fd;
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
data->version < NFS_MOUNT_VERSION ? "older" : "newer");
}
if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) {
printk("nfs_read_super: invalid file descriptor\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if (!S_ISSOCK(filp->f_inode->i_mode)) {
printk("nfs_read_super: not a socket\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
filp->f_count++;
lock_super(sb);
 
sb->s_blocksize = 1024; /* XXX */
sb->s_blocksize_bits = 10;
sb->s_magic = NFS_SUPER_MAGIC;
sb->s_dev = dev;
sb->s_op = &nfs_sops;
server = &sb->u.nfs_sb.s_server;
server->file = filp;
server->lock = 0;
server->wait = NULL;
server->flags = data->flags;
server->rsize = data->rsize;
if (server->rsize <= 0)
server->rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
else if (server->rsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
server->rsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
server->wsize = data->wsize;
if (server->wsize <= 0)
server->wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
else if (server->wsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
server->wsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
server->timeo = data->timeo*HZ/10;
server->retrans = data->retrans;
server->acregmin = data->acregmin*HZ;
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
strcpy(server->hostname, data->hostname);
 
/* Start of JSP NFS patch */
/* Check if passed address in data->addr */
if (data->addr.sin_addr.s_addr == INADDR_ANY) { /* No address passed */
if (((struct sockaddr_in *)(&server->toaddr))->sin_addr.s_addr == INADDR_ANY) {
printk("NFS: Error passed unconnected socket and no address\n") ;
MOD_DEC_USE_COUNT;
return NULL ;
} else {
/* Need access to socket internals JSP */
struct socket *sock;
int dummylen ;
 
/* printk("NFS: using socket address\n") ;*/
 
sock = &((filp->f_inode)->u.socket_i);
 
/* extract the other end of the socket into server->toaddr */
sock->ops->getname(sock, &(server->toaddr), &dummylen, 1) ;
}
} else {
/* printk("NFS: copying passed addr to server->toaddr\n") ;*/
memcpy((char *)&(server->toaddr),(char *)(&data->addr),sizeof(server->toaddr));
}
/* End of JSP NFS patch */
 
if ((server->rsock = rpc_makesock(filp)) == NULL) {
printk("NFS: cannot create RPC socket.\n");
MOD_DEC_USE_COUNT;
return NULL;
}
 
sb->u.nfs_sb.s_root = data->root;
unlock_super(sb);
if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) {
sb->s_dev = 0;
printk("nfs_read_super: get root inode failed\n");
MOD_DEC_USE_COUNT;
return NULL;
}
return sb;
}
 
void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
int error;
struct nfs_fsinfo res;
struct statfs tmp;
 
error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
&res);
if (error) {
printk("nfs_statfs: statfs error = %d\n", -error);
res.bsize = res.blocks = res.bfree = res.bavail = 0;
}
tmp.f_type = NFS_SUPER_MAGIC;
tmp.f_bsize = res.bsize;
tmp.f_blocks = res.blocks;
tmp.f_bfree = res.bfree;
tmp.f_bavail = res.bavail;
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
memcpy_tofs(buf, &tmp, bufsiz);
}
 
/*
* This is our own version of iget that looks up inodes by file handle
* instead of inode number. We use this technique instead of using
* the vfs read_inode function because there is no way to pass the
* file handle or current attributes into the read_inode function.
* We just have to be careful not to subvert iget's special handling
* of mount points.
*/
 
struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs_fattr newfattr;
int error;
struct inode *inode;
 
if (!sb) {
printk("nfs_fhget: super block is NULL\n");
return NULL;
}
if (!fattr) {
error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
&newfattr);
if (error) {
printk("nfs_fhget: getattr error = %d\n", -error);
return NULL;
}
fattr = &newfattr;
}
if (!(inode = iget(sb, fattr->fileid))) {
printk("nfs_fhget: iget failed\n");
return NULL;
}
if (inode->i_dev == sb->s_dev) {
if (inode->i_ino != fattr->fileid) {
printk("nfs_fhget: unexpected inode from iget\n");
return inode;
}
*NFS_FH(inode) = *fhandle;
nfs_refresh_inode(inode, fattr);
}
return inode;
}
 
int nfs_notify_change(struct inode *inode, struct iattr *attr)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
int error;
 
sattr.mode = (unsigned) -1;
if (attr->ia_valid & ATTR_MODE)
sattr.mode = attr->ia_mode;
 
sattr.uid = (unsigned) -1;
if (attr->ia_valid & ATTR_UID)
sattr.uid = attr->ia_uid;
 
sattr.gid = (unsigned) -1;
if (attr->ia_valid & ATTR_GID)
sattr.gid = attr->ia_gid;
 
 
sattr.size = (unsigned) -1;
if (attr->ia_valid & ATTR_SIZE)
sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1;
 
sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
if (attr->ia_valid & ATTR_MTIME) {
sattr.mtime.seconds = attr->ia_mtime;
sattr.mtime.useconds = 0;
}
 
sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
if (attr->ia_valid & ATTR_ATIME) {
sattr.atime.seconds = attr->ia_atime;
sattr.atime.useconds = 0;
}
 
error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
&sattr, &fattr);
if (!error)
nfs_refresh_inode(inode, &fattr);
inode->i_dirt = 0;
return error;
}
 
/* Every kernel module contains stuff like this. */
 
static struct file_system_type nfs_fs_type = {
nfs_read_super, "nfs", 0, NULL
};
 
/*
* Start up an nfsiod process. This is an awful hack, because when running
* as a module, we will keep insmod's memory. Besides, the current->comm
* hack won't work in this case
* The best would be to have a syscall for nfs client control that (among
* other things) forks biod's.
* Alternatively, we might want to have the idle task spawn biod's on demand.
*/
static int run_nfsiod(void *dummy)
{
int ret;
 
#ifdef __SMP__
lock_kernel();
syscall_count++;
#endif
 
MOD_INC_USE_COUNT;
exit_mm(current);
current->session = 1;
current->pgrp = 1;
sprintf(current->comm, "nfsiod");
#ifndef MODULE
current->blocked = ~0UL;
#endif
ret = nfsiod();
MOD_DEC_USE_COUNT;
return ret;
}
 
int init_nfs_fs(void)
{
/* Fork four biod's */
kernel_thread(run_nfsiod, NULL, 0);
kernel_thread(run_nfsiod, NULL, 0);
kernel_thread(run_nfsiod, NULL, 0);
kernel_thread(run_nfsiod, NULL, 0);
return register_filesystem(&nfs_fs_type);
}
 
#ifdef MODULE
int init_module(void)
{
int status;
 
if ((status = init_nfs_fs()) == 0)
register_symtab(0);
return status;
}
 
void cleanup_module(void)
{
unregister_filesystem(&nfs_fs_type);
nfs_kfree_cache();
}
 
#endif
/file.c
0,0 → 1,157
/*
* linux/fs/nfs/file.c
*
* Copyright (C) 1992 Rick Sladkey
*
* Changes Copyright (C) 1994 by Florian La Roche
* - Do not copy data too often around in the kernel.
* - In nfs_file_read the return value of kmalloc wasn't checked.
* - Put in a better version of read look-ahead buffering. Original idea
* and implementation by Wai S Kok elekokws@ee.nus.sg.
*
* Expire cache on write to a file by Wai S Kok (Oct 1994).
*
* Total rewrite of read side for new NFS buffer cache.. Linus.
*
* nfs regular file handling functions
*/
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/nfs_fs.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
static int nfs_file_mmap(struct inode *, struct file *, struct vm_area_struct *);
static int nfs_file_read(struct inode *, struct file *, char *, int);
static int nfs_file_write(struct inode *, struct file *, const char *, int);
static int nfs_fsync(struct inode *, struct file *);
 
static struct file_operations nfs_file_operations = {
NULL, /* lseek - default */
nfs_file_read, /* read */
nfs_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
nfs_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
nfs_fsync, /* fsync */
};
 
struct inode_operations nfs_file_inode_operations = {
&nfs_file_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
nfs_readpage, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL /* truncate */
};
 
static inline void revalidate_inode(struct nfs_server * server, struct inode * inode)
{
struct nfs_fattr fattr;
 
if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
return;
 
NFS_READTIME(inode) = jiffies;
if (nfs_proc_getattr(server, NFS_FH(inode), &fattr) == 0) {
nfs_refresh_inode(inode, &fattr);
if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
if ((NFS_ATTRTIMEO(inode) <<= 1) > server->acregmax)
NFS_ATTRTIMEO(inode) = server->acregmax;
return;
}
NFS_OLDMTIME(inode) = fattr.mtime.seconds;
}
invalidate_inode_pages(inode);
}
 
 
static int nfs_file_read(struct inode * inode, struct file * file,
char * buf, int count)
{
revalidate_inode(NFS_SERVER(inode), inode);
return generic_file_read(inode, file, buf, count);
}
 
static int nfs_file_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
revalidate_inode(NFS_SERVER(inode), inode);
return generic_file_mmap(inode, file, vma);
}
 
static int nfs_fsync(struct inode *inode, struct file *file)
{
return 0;
}
 
static int nfs_file_write(struct inode *inode, struct file *file, const char *buf,
int count)
{
int result, written, wsize;
struct nfs_fattr fattr;
unsigned long pos;
 
if (!inode) {
printk("nfs_file_write: inode = NULL\n");
return -EINVAL;
}
if (!S_ISREG(inode->i_mode)) {
printk("nfs_file_write: write to non-file, mode %07o\n",
inode->i_mode);
return -EINVAL;
}
if (count <= 0)
return 0;
 
pos = file->f_pos;
if (file->f_flags & O_APPEND)
pos = inode->i_size;
wsize = NFS_SERVER(inode)->wsize;
result = 0;
written = 0;
while (written < count) {
int hunk = count - written;
if (hunk >= wsize)
hunk = wsize;
result = nfs_proc_write(inode,
pos, hunk, buf, &fattr);
if (result < 0)
break;
pos += hunk;
buf += hunk;
written += hunk;
if (hunk < wsize)
break;
}
if (!written)
return result;
file->f_pos = pos;
if (pos > inode->i_size)
inode->i_size = pos;
/* Avoid possible Solaris 2.5 nfsd bug */
if (inode->i_ino == fattr.fileid)
nfs_refresh_inode(inode, &fattr);
return written;
}
 
/sock.c
0,0 → 1,127
/*
* linux/fs/nfs/sock.c
*
* Copyright (C) 1992, 1993 Rick Sladkey
*
* low-level nfs remote procedure call interface
*
* FIXES
*
* 2/7/94 James Bottomley and Jon Peatfield DAMTP, Cambridge University
*
* An xid mismatch no longer causes the request to be trashed.
*
* Peter Eriksson - incorrect XID used to confuse Linux
* Florian La Roche - use the correct max size, if reading a packet and
* also verify, if the whole packet has been read...
* more checks should be done in proc.c...
*
*/
 
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/rpcsock.h>
 
#include <asm/segment.h>
 
#define _S(nr) (1<<((nr)-1))
 
/*
* Place a synchronous call to the NFS server, meaning that the process
* sleeps in rpc_call until it either receives a reply or a major timeout
* occurs.
* This is now merely a front-end to nfs_rpc_doio.
*/
int
nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
{
struct rpc_ioreq req;
 
size += 1024; /* account for NFS slack space. ugly */
 
req.rq_addr = &server->toaddr;
req.rq_alen = sizeof(server->toaddr);
req.rq_slot = NULL;
 
req.rq_svec[0].iov_base = start;
req.rq_svec[0].iov_len = (end - start) << 2;
req.rq_slen = (end - start) << 2;
req.rq_snr = 1;
req.rq_rvec[0].iov_base = start;
req.rq_rvec[0].iov_len = size;
req.rq_rlen = size;
req.rq_rnr = 1;
 
return nfs_rpc_doio(server, &req, 0);
}
 
int
nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *req, int async)
{
struct rpc_timeout timeout;
unsigned long maxtimeo;
unsigned long oldmask;
int major_timeout_seen, result;
 
timeout.to_initval = server->timeo;
timeout.to_maxval = NFS_MAX_RPC_TIMEOUT*HZ/10;
timeout.to_retries = server->retrans;
timeout.to_exponential = 1;
 
oldmask = current->blocked;
current->blocked |= ~(_S(SIGKILL)
| ((server->flags & NFS_MOUNT_INTR)
? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL
? _S(SIGINT) : 0)
| (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
? _S(SIGQUIT) : 0))
: 0));
 
major_timeout_seen = 0;
maxtimeo = timeout.to_maxval;
 
do {
result = rpc_doio(server->rsock, req, &timeout, async);
rpc_release(server->rsock, req); /* Release slot */
 
if (current->signal & ~current->blocked)
result = -ERESTARTSYS;
if (result == -ETIMEDOUT) {
if (async)
break;
if (server->flags & NFS_MOUNT_SOFT) {
printk("NFS server %s not responding, "
"timed out.\n", server->hostname);
result = -EIO;
break;
}
if (!major_timeout_seen) {
printk("NFS server %s not responding, "
"still trying.\n", server->hostname);
major_timeout_seen = 1;
}
if ((timeout.to_initval <<= 1) >= maxtimeo) {
timeout.to_initval = maxtimeo;
}
} else if (result < 0 && result != -ERESTARTSYS) {
printk("NFS: notice message: result = %d.\n", result);
}
} while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT));
 
if (result >= 0 && major_timeout_seen)
printk("NFS server %s OK.\n", server->hostname);
/* 20 is the minimum RPC reply header size */
if (result >= 0 && result < 20) {
printk("NFS: too small read memory size (%d bytes)\n", result);
result = -EIO;
}
 
current->blocked = oldmask;
return result;
}
/nfsiod.c
0,0 → 1,124
/*
* linux/fs/nfs/nfsiod.c
*
* Async NFS RPC call support.
*
* When a process wants to place an asynchronous RPC call, it reserves
* an nfsiod slot, fills in all necessary fields including the callback
* handler field, and enqueues the request.
*
* This will wake up nfsiod, which calls nfs_rpc_doio to collect the
* reply. It then dispatches the result to the caller via the callback
* function, including result value and request pointer. It then re-inserts
* itself into the free list.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
 
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/rpcsock.h>
#include <linux/nfsiod.h>
 
static struct nfsiod_req * free_list = NULL;
static int active = 0;
 
#undef DEBUG_NFSIOD
#ifdef DEBUG_NFSIOD
#define dprintk(args...) printk(## args)
#else
#define dprintk(args...) /* nothing */
#endif
 
 
/*
* Reserve an nfsiod slot and initialize the request struct
*/
struct nfsiod_req *
nfsiod_reserve(struct nfs_server *server)
{
struct nfsiod_req *req;
 
if (!(req = free_list)) {
dprintk("BIO: nfsiod_reserve: no free nfsiods\n");
return NULL;
}
free_list = req->rq_next;
memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
 
if (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) {
dprintk("BIO: nfsiod_reserve failed to reserve RPC slot\n");
req->rq_next = free_list;
free_list = req;
return NULL;
}
 
req->rq_server = server;
return req;
}
 
void
nfsiod_release(struct nfsiod_req *req)
{
dprintk("BIO: nfsiod_release called\n");
rpc_release(req->rq_server->rsock, &req->rq_rpcreq);
memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
req->rq_next = free_list;
free_list = req;
}
 
/*
* Transmit a request and put it on nfsiod's list of pending requests.
*/
void
nfsiod_enqueue(struct nfsiod_req *req)
{
dprintk("BIO: enqueuing request %p\n", &req->rq_rpcreq);
wake_up(&req->rq_wait);
schedule();
}
 
/*
* This is the main nfsiod loop.
*/
int
nfsiod(void)
{
struct nfsiod_req request, *req = &request;
int result;
 
dprintk("BIO: nfsiod %d starting\n", current->pid);
while (1) {
/* Insert request into free list */
memset(req, 0, sizeof(*req));
req->rq_next = free_list;
free_list = req;
 
/* Wait until user enqueues request */
dprintk("BIO: before: now %d nfsiod's active\n", active);
dprintk("BIO: nfsiod %d waiting\n", current->pid);
#ifndef MODULE
current->signal = 0;
#endif
interruptible_sleep_on(&req->rq_wait);
#ifdef MODULE
if (current->signal & ~current->blocked)
break;
#endif
if (!req->rq_rpcreq.rq_slot)
continue;
dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
current->pid);
active++;
dprintk("BIO: before: now %d nfsiod's active\n", active);
do {
result = nfs_rpc_doio(req->rq_server,
&req->rq_rpcreq, 1);
} while (!req->rq_callback(result, req));
active--;
}
 
return 0;
}
/proc.c
0,0 → 1,1044
/*
* linux/fs/nfs/proc.c
*
* Copyright (C) 1992, 1993, 1994 Rick Sladkey
*
* OS-independent nfs remote procedure call functions
*
* Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
* so at last we can have decent(ish) throughput off a
* Sun server.
*
* Coding optimized and cleaned up by Florian La Roche.
* Note: Error returns are optimized for NFS_OK, which isn't translated via
* nfs_stat_to_errno(), but happens to be already the right return code.
*
* FixMe: We ought to define a sensible small max size for
* things like getattr that are tiny packets and use the
* old get_free_page stuff with it.
*
* Also, the code currently doesn't check the size of the packet, when
* it decodes the packet.
*
* Feel free to fix it and mail me the diffs if it worries you.
*/
 
/*
* Fixes:
* Ion Badulescu <ionut@cs.columbia.edu> : FIFO's need special handling in NFSv2
*/
 
/*
* Defining NFS_PROC_DEBUG causes a lookup of a file named
* "xyzzy" to toggle debugging. Just cd to an NFS-mounted
* filesystem and type 'ls xyzzy' to turn on debugging.
*/
 
#if 0
#define NFS_PROC_DEBUG
#endif
 
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/nfs_fs.h>
#include <linux/utsname.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/in.h>
#include <linux/pagemap.h>
 
#include <asm/segment.h>
 
#ifdef NFS_PROC_DEBUG
 
static int proc_debug = 0;
#define PRINTK(format, args...) \
do { \
if (proc_debug) \
printk(format , ## args); \
} while (0)
 
#else /* !NFS_PROC_DEBUG */
 
#define PRINTK(format, args...) do ; while (0)
 
#endif /* !NFS_PROC_DEBUG */
 
/* Mapping from NFS error code to "errno" error code. */
#define errno_NFSERR_IO EIO
 
static int *nfs_rpc_header(int *p, int procedure, int ruid);
static int *nfs_rpc_verify(int *p);
static int nfs_stat_to_errno(int stat);
 
/*
* Our memory allocation and release functions.
*/
#define NFS_SLACK_SPACE 1024 /* Total overkill */
/* !!! Be careful, this constant is now also used in sock.c...
We should easily convert to not using it anymore for most cases... */
 
static inline int *nfs_rpc_alloc(int size)
{
int *i;
 
while (!(i = (int *)kmalloc(size+NFS_SLACK_SPACE,GFP_NFS))) {
schedule();
}
return i;
}
 
static inline void nfs_rpc_free(int *p)
{
kfree((void *)p);
}
 
/*
* Here are a bunch of xdr encode/decode functions that convert
* between machine dependent and xdr data formats.
*/
 
#define QUADLEN(len) (((len) + 3) >> 2)
 
static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle)
{
*((struct nfs_fh *) p) = *fhandle;
return p + QUADLEN(sizeof(*fhandle));
}
 
static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
{
*fhandle = *((struct nfs_fh *) p);
return p + QUADLEN(sizeof(*fhandle));
}
 
static inline int *xdr_encode_string(int *p, const char *string)
{
int len = strlen(string);
int quadlen = QUADLEN(len);
 
p[quadlen] = 0;
*p++ = htonl(len);
memcpy(p, string, len);
return p + quadlen;
}
 
static inline int *xdr_decode_string(int *p, char *string, unsigned int maxlen)
{
unsigned int len = ntohl(*p++);
if (len > maxlen)
return NULL;
memcpy(string, p, len);
string[len] = '\0';
return p + QUADLEN(len);
}
 
static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len,
unsigned int maxlen)
{
*len = ntohl(*p++);
if (*len > maxlen)
return NULL;
*string = (char *) p;
return p + QUADLEN(*len);
}
 
 
static inline int *xdr_encode_data(int *p, const char *data, int len)
{
int quadlen = QUADLEN(len);
p[quadlen] = 0;
*p++ = htonl(len);
memcpy_fromfs(p, data, len);
return p + quadlen;
}
 
static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
{
unsigned len = *lenp = ntohl(*p++);
if (len > maxlen)
return NULL;
memcpy(data, p, len);
return p + QUADLEN(len);
}
 
static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
{
fattr->type = (enum nfs_ftype) ntohl(*p++);
fattr->mode = ntohl(*p++);
fattr->nlink = ntohl(*p++);
fattr->uid = ntohl(*p++);
fattr->gid = ntohl(*p++);
fattr->size = ntohl(*p++);
fattr->blocksize = ntohl(*p++);
fattr->rdev = ntohl(*p++);
fattr->blocks = ntohl(*p++);
fattr->fsid = ntohl(*p++);
fattr->fileid = ntohl(*p++);
fattr->atime.seconds = ntohl(*p++);
fattr->atime.useconds = ntohl(*p++);
fattr->mtime.seconds = ntohl(*p++);
fattr->mtime.useconds = ntohl(*p++);
fattr->ctime.seconds = ntohl(*p++);
fattr->ctime.useconds = ntohl(*p++);
if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) {
fattr->type = NFFIFO;
fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
fattr->rdev = 0;
}
return p;
}
 
static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr)
{
*p++ = htonl(sattr->mode);
*p++ = htonl(sattr->uid);
*p++ = htonl(sattr->gid);
*p++ = htonl(sattr->size);
*p++ = htonl(sattr->atime.seconds);
*p++ = htonl(sattr->atime.useconds);
*p++ = htonl(sattr->mtime.seconds);
*p++ = htonl(sattr->mtime.useconds);
return p;
}
 
static int *xdr_decode_entry(int *p, struct nfs_entry *entry)
{
entry->fileid = ntohl(*p++);
if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN)))
return NULL;
entry->cookie = ntohl(*p++);
entry->eof = 0;
return p;
}
 
static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res)
{
res->tsize = ntohl(*p++);
res->bsize = ntohl(*p++);
res->blocks = ntohl(*p++);
res->bfree = ntohl(*p++);
res->bavail = ntohl(*p++);
return p;
}
 
/*
* One function for each procedure in the NFS protocol.
*/
 
int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call getattr\n");
if (!(p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply getattr\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply getattr failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_sattr *sattr, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call setattr\n");
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
p = xdr_encode_fhandle(p, fhandle);
p = xdr_encode_sattr(p, sattr);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply setattr\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply setattr failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call lookup %s\n", name);
#ifdef NFS_PROC_DEBUG
if (!strcmp(name, "xyzzy"))
proc_debug = 1 - proc_debug;
#endif
if (!(p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fhandle(p, fhandle);
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply lookup\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply lookup failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
int **p0, char **string, unsigned int *len, unsigned int maxlen)
{
int *p;
int status, ruid = 0;
 
PRINTK("NFS call readlink\n");
if (!(*p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(*p0, NFSPROC_READLINK, ruid);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, *p0, p, server->rsize)) < 0)
return status;
if (!(p = nfs_rpc_verify(*p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
if (!(p = xdr_decode_string2(p, string, len, maxlen))) {
printk("nfs_proc_readlink: giant pathname\n");
status = -errno_NFSERR_IO;
}
else /* status = 0, */
PRINTK("NFS reply readlink\n");
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply readlink failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
return status;
}
 
int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
int offset, int count, char *data, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
int len;
 
PRINTK("NFS call read %d @ %d\n", count, offset);
if (!(p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(offset);
*p++ = htonl(count);
*p++ = htonl(count); /* traditional, could be any value */
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fattr(p, fattr);
if (!(p = xdr_decode_data(p, data, &len, count))) {
printk("nfs_proc_read: giant data size\n");
status = -errno_NFSERR_IO;
}
else {
status = len;
PRINTK("NFS reply read %d\n", len);
}
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply read failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int
nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
struct nfs_fh *fh, unsigned long offset,
unsigned long count, __u32 *buf)
{
__u32 *p, *p0;
int len;
 
PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
return -EIO;
 
p = nfs_rpc_header(p0, NFSPROC_READ, 0);
p = xdr_encode_fhandle(p, fh);
*p++ = htonl(offset);
*p++ = htonl(count);
*p++ = htonl(count); /* traditional, could be any value */
req->rq_svec[0].iov_base = p0;
req->rq_svec[0].iov_len = (p - p0) << 2;
req->rq_slen = (p - p0) << 2;
req->rq_snr = 1;
 
len = (6 + 1 + 17 + 1); /* standard READ reply header */
req->rq_rvec[0].iov_base = p0;
req->rq_rvec[0].iov_len = len << 2;
req->rq_rvec[1].iov_base = buf;
req->rq_rvec[1].iov_len = count;
req->rq_rvec[2].iov_base = p0 + len; /* spill buffer */
req->rq_rvec[2].iov_len = (NFS_SLACK_SPACE - len) << 2;
req->rq_rlen = count + NFS_SLACK_SPACE;
req->rq_rnr = 3;
 
req->rq_addr = &server->toaddr;
req->rq_alen = sizeof(server->toaddr);
 
return rpc_transmit(server->rsock, req);
}
 
int
nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr)
{
int status;
__u32 *p0, *p;
int count;
 
p0 = (__u32 *) req->rq_rvec[0].iov_base;
 
if (!(p = nfs_rpc_verify(p0))) {
/* Tell the upper layers to retry */
status = -EAGAIN;
/* status = -errno_NFSERR_IO; */
} else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fattr(p, fattr);
count = ntohl(*p++);
if (p != req->rq_rvec[2].iov_base) {
/* unexpected RPC reply header size. punt.
* fixme: move iovec contents to align data
* on page boundary and adjust RPC header size
* guess. */
status = -errno_NFSERR_IO;
PRINTK("NFS reply read odd header size %d\n",
(p - p0) << 2);
} else {
status = count;
PRINTK("NFS reply read %d\n", count);
}
}
else {
PRINTK("NFS reply read failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_write(struct inode * inode, int offset,
int count, const char *data, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
void * kdata; /* address of kernel copy */
struct nfs_server * server = NFS_SERVER(inode);
struct nfs_fh *fhandle = NFS_FH(inode);
 
PRINTK("NFS call write %d @ %d\n", count, offset);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(offset); /* traditional, could be any value */
*p++ = htonl(offset);
*p++ = htonl(count); /* traditional, could be any value */
kdata = (void *) (p+1); /* start of data in RPC buffer */
p = xdr_encode_data(p, data, count);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
update_vm_cache(inode, offset, kdata, count);
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply write\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply write failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call create %s\n", name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_sattr(p, sattr);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fhandle(p, fhandle);
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply create\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply create failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call remove %s\n", name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
PRINTK("NFS reply remove\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply remove failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_rename(struct nfs_server *server,
struct nfs_fh *old_dir, const char *old_name,
struct nfs_fh *new_dir, const char *new_name,
int must_be_dir)
{
int *p, *p0;
int status;
int ruid = 0;
 
/*
* Disallow "rename()" with trailing slashes over NFS: getting
* POSIX.1 behaviour is just too unlikely.
*/
if (must_be_dir)
return -EINVAL;
PRINTK("NFS call rename %s -> %s\n", old_name, new_name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
p = xdr_encode_fhandle(p, old_dir);
p = xdr_encode_string(p, old_name);
p = xdr_encode_fhandle(p, new_dir);
p = xdr_encode_string(p, new_name);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
PRINTK("NFS reply rename\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply rename failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call link %s\n", name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
p = xdr_encode_fhandle(p, fhandle);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
PRINTK("NFS reply link\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply link failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
const char *name, const char *path, struct nfs_sattr *sattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call symlink %s -> %s\n", name, path);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_string(p, path);
p = xdr_encode_sattr(p, sattr);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
PRINTK("NFS reply symlink\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply symlink failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call mkdir %s\n", name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_sattr(p, sattr);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fhandle(p, fhandle);
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply mkdir\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply mkdir failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call rmdir %s\n", name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
PRINTK("NFS reply rmdir\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply rmdir failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
int cookie, int count, struct nfs_entry *entry)
{
int *p, *p0;
int status;
int ruid = 0;
int i;
int size;
int eof;
 
PRINTK("NFS call readdir %d @ %d\n", count, cookie);
size = server->rsize;
if (!(p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(cookie);
*p++ = htonl(size);
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
for (i = 0; i < count && *p++; i++) {
if (!(p = xdr_decode_entry(p, entry++)))
break;
}
if (!p) {
printk("nfs_proc_readdir: giant filename\n");
status = -errno_NFSERR_IO;
}
else {
eof = (i == count && !*p++ && *p++)
|| (i < count && *p++);
if (eof && i)
entry[-1].eof = 1;
PRINTK("NFS reply readdir %d %s\n", i,
eof ? "eof" : "");
status = i;
}
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply readdir failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *res)
{
int *p, *p0;
int status;
int ruid = 0;
 
PRINTK("NFS call statfs\n");
if (!(p0 = nfs_rpc_alloc(server->rsize)))
return -EIO;
retry:
p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
nfs_rpc_free(p0);
return status;
}
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fsinfo(p, res);
PRINTK("NFS reply statfs\n");
/* status = 0; */
}
else {
if (!ruid && current->fsuid == 0 && current->uid != 0) {
ruid = 1;
goto retry;
}
PRINTK("NFS reply statfs failed = %d\n", status);
status = -nfs_stat_to_errno(status);
}
nfs_rpc_free(p0);
return status;
}
 
/*
* Here are a few RPC-assist functions.
*/
 
int *rpc_header(int *p, int procedure, int program, int version,
int uid, int gid, int *groups)
{
int *p1, *p2;
int i;
static int xid = 0;
unsigned char *sys = (unsigned char *) system_utsname.nodename;
 
if (xid == 0) {
xid = CURRENT_TIME;
xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0];
}
*p++ = htonl(++xid);
*p++ = htonl(RPC_CALL);
*p++ = htonl(RPC_VERSION);
*p++ = htonl(program);
*p++ = htonl(version);
*p++ = htonl(procedure);
*p++ = htonl(RPC_AUTH_UNIX);
p1 = p++;
*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
p = xdr_encode_string(p, (char *) sys);
*p++ = htonl(uid);
*p++ = htonl(gid);
p2 = p++;
for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++)
*p++ = htonl(groups[i]);
*p2 = htonl(i);
*p1 = htonl((p - (p1 + 1)) << 2);
*p++ = htonl(RPC_AUTH_NULL);
*p++ = htonl(0);
return p;
}
 
 
static int *nfs_rpc_header(int *p, int procedure, int ruid)
{
return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
(ruid ? current->uid : current->fsuid),
current->egid, current->groups);
}
 
 
int *rpc_verify(int *p)
{
unsigned int n;
 
p++;
if ((n = ntohl(*p++)) != RPC_REPLY) {
printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
return NULL;
}
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
printk("nfs_rpc_verify: RPC call rejected: %d\n", n);
return NULL;
}
switch (n = ntohl(*p++)) {
case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT:
break;
default:
printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n);
return NULL;
}
if ((n = ntohl(*p++)) > 400) {
printk("nfs_rpc_verify: giant auth size\n");
return NULL;
}
p += QUADLEN(n);
if ((n = ntohl(*p++)) != RPC_SUCCESS) {
printk("nfs_rpc_verify: RPC call failed: %d\n", n);
return NULL;
}
return p;
}
 
 
static int *nfs_rpc_verify(int *p)
{
return rpc_verify(p);
}
 
 
/*
* We need to translate between nfs status return values and
* the local errno values which may not be the same.
*/
 
static struct {
int stat;
int errno;
} nfs_errtbl[] = {
{ NFS_OK, 0 },
{ NFSERR_PERM, EPERM },
{ NFSERR_NOENT, ENOENT },
{ NFSERR_IO, errno_NFSERR_IO },
{ NFSERR_NXIO, ENXIO },
{ NFSERR_EAGAIN, EAGAIN },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
{ NFSERR_XDEV, EXDEV },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
{ NFSERR_INVAL, EINVAL },
{ NFSERR_FBIG, EFBIG },
{ NFSERR_NOSPC, ENOSPC },
{ NFSERR_ROFS, EROFS },
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
{ NFSERR_NOTEMPTY, ENOTEMPTY },
{ NFSERR_DQUOT, EDQUOT },
{ NFSERR_STALE, ESTALE },
#ifdef EWFLUSH
{ NFSERR_WFLUSH, EWFLUSH },
#endif
{ -1, EIO }
};
 
static int nfs_stat_to_errno(int stat)
{
int i;
 
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno;
}
printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
return nfs_errtbl[i].errno;
}
 
/rpcsock.c
0,0 → 1,584
/*
* linux/fs/nfs/rpcsock.c
*
* This is a generic RPC call interface for datagram sockets that is able
* to place several concurrent RPC requests at the same time. It works like
* this:
*
* - When a process places a call, it allocates a request slot if
* one is available. Otherwise, it sleeps on the backlog queue
* (rpc_reserve).
* - Then, the message is transmitted via rpc_send (exported by name of
* rpc_transmit).
* - Finally, the process waits for the call to complete (rpc_doio):
* The first process on the receive queue waits for the next RPC packet,
* and peeks at the XID. If it finds a matching request, it receives
* the datagram on behalf of that process and wakes it up. Otherwise,
* the datagram is discarded.
* - If the process having received the datagram was the first one on
* the receive queue, it wakes up the next one to listen for replies.
* - It then removes itself from the request queue (rpc_release).
* If there are more callers waiting on the backlog queue, they are
* woken up, too.
*
* Mar 1996:
* - Split up large functions into smaller chunks as per Linus' coding
* style. Found an interesting bug this way, too.
* - Added entry points for nfsiod.
*
* Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
*/
 
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/rpcsock.h>
 
#include <linux/udp.h>
#include <net/sock.h>
 
#include <asm/segment.h>
 
#define msleep(sec) { current->timeout = sec * HZ / 1000; \
current->state = TASK_INTERRUPTIBLE; \
schedule(); \
}
 
#undef DEBUG_RPC
#ifdef DEBUG_RPC
#define dprintk(args...) printk(## args)
#else
#define dprintk(args...)
#endif
 
 
/*
* Insert new request into wait list. We make sure list is sorted by
* increasing timeout value.
*/
static inline void
rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot)
{
struct rpc_wait *next = rsock->pending;
 
slot->w_next = next;
slot->w_prev = NULL;
if (next)
next->w_prev = slot;
rsock->pending = slot;
slot->w_queued = 1;
 
dprintk("RPC: inserted %p into queue\n", slot);
}
 
/*
* Remove request from request queue
*/
static inline void
rpc_remque(struct rpc_sock *rsock, struct rpc_wait *slot)
{
struct rpc_wait *prev = slot->w_prev,
*next = slot->w_next;
 
if (prev != NULL)
prev->w_next = next;
else
rsock->pending = next;
if (next != NULL)
next->w_prev = prev;
 
slot->w_queued = 0;
dprintk("RPC: removed %p from queue, head now %p.\n",
slot, rsock->pending);
}
 
/*
* Write data to socket.
*/
static inline int
rpc_sendmsg(struct rpc_sock *rsock, struct iovec *iov, int nr, int len,
struct sockaddr *sap, int salen)
{
struct socket *sock = rsock->sock;
struct msghdr msg;
unsigned long oldfs;
int result;
 
msg.msg_iov = iov;
msg.msg_iovlen = nr;
msg.msg_name = sap;
msg.msg_namelen = salen;
msg.msg_control = NULL;
 
oldfs = get_fs();
set_fs(get_ds());
result = sock->ops->sendmsg(sock, &msg, len, 0, 0);
set_fs(oldfs);
 
dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result);
return result;
}
/*
* Read data from socket
*/
static inline int
rpc_recvmsg(struct rpc_sock *rsock, struct iovec *iov,
int nr, int len, int flags)
{
struct socket *sock = rsock->sock;
struct sockaddr sa;
struct msghdr msg;
unsigned long oldfs;
int result, alen;
 
msg.msg_iov = iov;
msg.msg_iovlen = nr;
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_control = NULL;
 
oldfs = get_fs();
set_fs(get_ds());
result = sock->ops->recvmsg(sock, &msg, len, 1, flags, &alen);
set_fs(oldfs);
 
dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result);
return result;
}
 
/*
* This code is slightly complicated. Since the networking code does not
* honor the current->timeout value, we have to select on the socket.
*/
static inline int
rpc_select(struct rpc_sock *rsock)
{
struct select_table_entry entry;
struct file *file = rsock->file;
select_table wait_table;
 
dprintk("RPC: selecting on socket...\n");
wait_table.nr = 0;
wait_table.entry = &entry;
current->state = TASK_INTERRUPTIBLE;
if (!file->f_op->select(file->f_inode, file, SEL_IN, &wait_table)
&& !file->f_op->select(file->f_inode, file, SEL_IN, NULL)) {
schedule();
remove_wait_queue(entry.wait_address, &entry.wait);
current->state = TASK_RUNNING;
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
if (current->timeout == 0)
return -ETIMEDOUT;
} else if (wait_table.nr)
remove_wait_queue(entry.wait_address, &entry.wait);
current->state = TASK_RUNNING;
dprintk("RPC: ...Okay, there appears to be some data.\n");
return 0;
}
 
/*
* Reserve an RPC call slot. nocwait determines whether we wait in case
* of congestion or not.
*/
int
rpc_reserve(struct rpc_sock *rsock, struct rpc_ioreq *req, int nocwait)
{
struct rpc_wait *slot;
 
req->rq_slot = NULL;
 
while (!(slot = rsock->free) || rsock->cong >= rsock->cwnd) {
if (nocwait) {
current->timeout = 0;
return -ENOBUFS;
}
dprintk("RPC: rpc_reserve waiting on backlog\n");
interruptible_sleep_on(&rsock->backlog);
if (current->timeout == 0)
return -ETIMEDOUT;
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
if (rsock->shutdown)
return -EIO;
}
 
rsock->free = slot->w_next;
rsock->cong += RPC_CWNDSCALE; /* bump congestion value */
 
slot->w_queued = 0;
slot->w_gotit = 0;
slot->w_req = req;
 
dprintk("RPC: reserved slot %p\n", slot);
req->rq_slot = slot;
return 0;
}
 
/*
* Release an RPC call slot
*/
void
rpc_release(struct rpc_sock *rsock, struct rpc_ioreq *req)
{
struct rpc_wait *slot = req->rq_slot;
 
if (slot != NULL) {
dprintk("RPC: release slot %p\n", slot);
 
/* Wake up the next receiver */
if (slot == rsock->pending && slot->w_next != NULL)
wake_up(&slot->w_next->w_wait);
 
/* remove slot from queue of pending */
if (slot->w_queued)
rpc_remque(rsock, slot);
slot->w_next = rsock->free;
rsock->free = slot;
 
/* decrease congestion value */
rsock->cong -= RPC_CWNDSCALE;
if (rsock->cong < rsock->cwnd && rsock->backlog)
wake_up(&rsock->backlog);
if (rsock->shutdown)
wake_up(&rsock->shutwait);
 
req->rq_slot = NULL;
}
}
 
/*
* Adjust RPC congestion window
*/
static void
rpc_cwnd_adjust(struct rpc_sock *rsock, int timeout)
{
unsigned long cwnd = rsock->cwnd;
 
if (!timeout) {
if (rsock->cong >= cwnd) {
/* The (cwnd >> 1) term makes sure
* the result gets rounded properly. */
cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE +
(cwnd >> 1)) / cwnd;
if (cwnd > RPC_MAXCWND)
cwnd = RPC_MAXCWND;
}
} else {
if ((cwnd >>= 1) < RPC_CWNDSCALE)
cwnd = RPC_CWNDSCALE;
dprintk("RPC: cwnd decrease %08lx\n", cwnd);
}
dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx\n",
rsock->cong, rsock->cwnd, cwnd);
 
rsock->cwnd = cwnd;
}
 
static inline void
rpc_send_check(char *where, u32 *ptr)
{
if (ptr[1] != htonl(RPC_CALL) || ptr[2] != htonl(RPC_VERSION)) {
printk("RPC: %s sending evil packet:\n"
" %08x %08x %08x %08x %08x %08x %08x %08x\n",
where,
ptr[0], ptr[1], ptr[2], ptr[3],
ptr[4], ptr[5], ptr[6], ptr[7]);
}
}
 
/*
* Place the actual RPC call.
* We have to copy the iovec because sendmsg fiddles with its contents.
*/
static inline int
rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
{
struct rpc_ioreq *req = slot->w_req;
struct iovec iov[UIO_MAXIOV];
 
if (rsock->shutdown)
return -EIO;
 
memcpy(iov, req->rq_svec, req->rq_snr * sizeof(iov[0]));
slot->w_xid = *(u32 *)(iov[0].iov_base);
if (!slot->w_queued)
rpc_insque(rsock, slot);
 
dprintk("rpc_send(%p, %x)\n", slot, slot->w_xid);
rpc_send_check("rpc_send", (u32 *) req->rq_svec[0].iov_base);
return rpc_sendmsg(rsock, iov, req->rq_snr, req->rq_slen,
req->rq_addr, req->rq_alen);
}
 
/*
* This is the same as rpc_send but for the functions exported to nfsiod
*/
int
rpc_transmit(struct rpc_sock *rsock, struct rpc_ioreq *req)
{
rpc_send_check("rpc_transmit", (u32 *) req->rq_svec[0].iov_base);
return rpc_send(rsock, req->rq_slot);
}
 
/*
* Receive and dispatch a single reply
*/
static inline int
rpc_grok(struct rpc_sock *rsock)
{
struct rpc_wait *rovr;
struct rpc_ioreq *req;
struct iovec iov[UIO_MAXIOV];
u32 xid;
int safe, result;
 
iov[0].iov_base = (void *) &xid;
iov[0].iov_len = sizeof(xid);
result = rpc_recvmsg(rsock, iov, 1, sizeof(xid), MSG_PEEK);
 
if (result < 0) {
switch (-result) {
case EAGAIN: case ECONNREFUSED:
return 0;
case ERESTARTSYS:
return result;
default:
dprintk("rpc_grok: recv error = %d\n", result);
}
}
if (result < 4) {
printk(KERN_WARNING "RPC: impossible RPC reply size %d\n",
result);
iov[0].iov_base=(void*)&xid; /* xid=32bits, which is large enough */
iov[0].iov_len=result;
rpc_recvmsg(rsock, iov, 1, result, 0);
return 0;
}
 
dprintk("RPC: rpc_grok: got xid %08lx\n", (unsigned long) xid);
 
/* Look for the caller */
safe = 0;
for (rovr = rsock->pending; rovr; rovr = rovr->w_next) {
if (rovr->w_xid == xid)
break;
if (safe++ > RPC_MAXREQS) {
printk(KERN_WARNING "RPC: loop in request Q!!\n");
rovr = NULL;
break;
}
}
 
if (!rovr || rovr->w_gotit) {
/* discard dgram */
dprintk("RPC: rpc_grok: %s.\n",
rovr? "duplicate reply" : "bad XID");
iov[0].iov_base = (void *) &xid;
iov[0].iov_len = sizeof(xid);
rpc_recvmsg(rsock, iov, 1, sizeof(xid), 0);
return 0;
}
req = rovr->w_req;
 
/* Now receive the reply... Copy the iovec first because of
* memcpy_fromiovec fiddling. */
memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
result = rpc_recvmsg(rsock, iov, req->rq_rnr, req->rq_rlen, 0);
rovr->w_result = result;
rovr->w_gotit = 1;
 
/* ... and wake up the process */
wake_up(&rovr->w_wait);
 
return result;
}
 
/*
* Wait for the reply to our call.
*/
static int
rpc_recv(struct rpc_sock *rsock, struct rpc_wait *slot)
{
int result;
 
do {
/* If we are not the receiver, wait on the sidelines */
dprintk("RPC: rpc_recv TP1\n");
while (rsock->pending != slot) {
if (!slot->w_gotit)
interruptible_sleep_on(&slot->w_wait);
if (slot->w_gotit)
return slot->w_result; /* quite important */
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
if (rsock->shutdown)
return -EIO;
if (current->timeout == 0)
return -ETIMEDOUT;
}
 
/* Wait for data to arrive */
if ((result = rpc_select(rsock)) < 0) {
dprintk("RPC: select error = %d\n", result);
return result;
}
 
/* Receive and dispatch */
if ((result = rpc_grok(rsock)) < 0)
return result;
} while (current->timeout && !slot->w_gotit);
 
return slot->w_gotit? slot->w_result : -ETIMEDOUT;
}
 
/*
* Generic RPC call routine. This handles retries and timeouts etc pp.
*
* If sent is non-null, it assumes the called has already sent out the
* message, so it won't need to do so unless a timeout occurs.
*/
int
rpc_doio(struct rpc_sock *rsock, struct rpc_ioreq *req,
struct rpc_timeout *strategy, int sent)
{
struct rpc_wait *slot;
int result, retries;
unsigned long timeout;
 
timeout = strategy->to_initval;
retries = 0;
slot = req->rq_slot;
 
do {
dprintk("RPC: rpc_doio: TP1 (req %p)\n", req);
current->timeout = jiffies + timeout;
if (slot == NULL) {
result = rpc_reserve(rsock, req, 0);
if (result == -ETIMEDOUT)
goto timedout;
if (result < 0)
break;
slot = req->rq_slot;
rpc_send_check("rpc_doio",
(u32 *) req->rq_svec[0].iov_base);
rpc_insque(rsock, slot);
}
 
/* This check is for loopback NFS. Sometimes replies come
* in before biod has called rpc_doio... */
if (slot->w_gotit) {
result = slot->w_result;
break;
}
 
dprintk("RPC: rpc_doio: TP2\n");
if (sent || (result = rpc_send(rsock, slot)) >= 0) {
result = rpc_recv(rsock, slot);
sent = 0;
}
 
if (result != -ETIMEDOUT) {
/* dprintk("RPC: rpc_recv returned %d\n", result); */
rpc_cwnd_adjust(rsock, 0);
break;
}
 
rpc_cwnd_adjust(rsock, 1);
 
timedout:
dprintk("RPC: rpc_recv returned timeout.\n");
if (strategy->to_exponential)
timeout <<= 1;
else
timeout += strategy->to_increment;
if (strategy->to_maxval && timeout >= strategy->to_maxval)
timeout = strategy->to_maxval;
if (strategy->to_retries && ++retries >= strategy->to_retries)
break;
} while (1);
 
dprintk("RPC: rpc_doio: TP3\n");
current->timeout = 0;
return result;
}
 
/*
*/
int
rpc_call(struct rpc_sock *rsock, struct rpc_ioreq *req,
struct rpc_timeout *strategy)
{
int result;
 
result = rpc_doio(rsock, req, strategy, 0);
if (req->rq_slot == NULL)
printk(KERN_WARNING "RPC: bad: rq_slot == NULL\n");
rpc_release(rsock, req);
return result;
}
 
struct rpc_sock *
rpc_makesock(struct file *file)
{
struct rpc_sock *rsock;
struct socket *sock;
struct sock *sk;
struct rpc_wait *slot;
int i;
 
dprintk("RPC: make RPC socket...\n");
sock = &file->f_inode->u.socket_i;
if (sock->type != SOCK_DGRAM || sock->ops->family != AF_INET) {
printk(KERN_WARNING "RPC: only UDP sockets supported\n");
return NULL;
}
sk = (struct sock *) sock->data;
 
if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
return NULL;
memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
 
rsock->sock = sock;
rsock->inet = sk;
rsock->file = file;
rsock->cwnd = RPC_INITCWND;
 
dprintk("RPC: slots %p, %p, ...\n", rsock->waiting, rsock->waiting + 1);
rsock->free = rsock->waiting;
for (i = 0, slot = rsock->waiting; i < RPC_MAXREQS-1; i++, slot++)
slot->w_next = slot + 1;
slot->w_next = NULL;
 
dprintk("RPC: made socket %p\n", rsock);
return rsock;
}
 
int
rpc_closesock(struct rpc_sock *rsock)
{
unsigned long t0 = jiffies;
 
rsock->shutdown = 1;
while (rsock->pending || waitqueue_active(&rsock->backlog)) {
interruptible_sleep_on(&rsock->shutwait);
if (current->signal & ~current->blocked)
return -EINTR;
#if 1
if (t0 && t0 - jiffies > 60 * HZ) {
printk(KERN_WARNING "RPC: hanging in rpc_closesock.\n");
t0 = 0;
}
#endif
}
 
kfree(rsock);
return 0;
}
/symlink.c
0,0 → 1,118
/*
* linux/fs/nfs/symlink.c
*
* Copyright (C) 1992 Rick Sladkey
*
* Optimization changes Copyright (C) 1994 Florian La Roche
*
* nfs symlink handling code
*/
 
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/nfs_fs.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/string.h>
 
#include <asm/segment.h>
 
static int nfs_readlink(struct inode *, char *, int);
static int nfs_follow_link(struct inode *, struct inode *, int, int,
struct inode **);
 
/*
* symlinks can't do much...
*/
struct inode_operations nfs_symlink_inode_operations = {
NULL, /* no file-operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
nfs_readlink, /* readlink */
nfs_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
 
static int nfs_follow_link(struct inode *dir, struct inode *inode,
int flag, int mode, struct inode **res_inode)
{
int error, *mem;
unsigned int len;
char *res, *res2;
 
*res_inode = NULL;
if (!dir) {
dir = current->fs->root;
dir->i_count++;
}
if (!inode) {
iput(dir);
return -ENOENT;
}
if (!S_ISLNK(inode->i_mode)) {
iput(dir);
*res_inode = inode;
return 0;
}
if (current->link_count > 5) {
iput(inode);
iput(dir);
return -ELOOP;
}
error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
&res, &len, NFS_MAXPATHLEN);
if (error) {
iput(inode);
iput(dir);
kfree(mem);
return error;
}
while ((res2 = (char *) kmalloc(NFS_MAXPATHLEN + 1, GFP_NFS)) == NULL) {
schedule();
}
memcpy(res2, res, len);
res2[len] = '\0';
kfree(mem);
iput(inode);
current->link_count++;
error = open_namei(res2, flag, mode, res_inode, dir);
current->link_count--;
kfree_s(res2, NFS_MAXPATHLEN + 1);
return error;
}
 
static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
{
int error, *mem;
unsigned int len;
char *res;
 
if (!S_ISLNK(inode->i_mode)) {
iput(inode);
return -EINVAL;
}
if (buflen > NFS_MAXPATHLEN)
buflen = NFS_MAXPATHLEN;
error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
&res, &len, buflen);
iput(inode);
if (! error) {
memcpy_tofs(buffer, res, len);
put_user('\0', buffer + len);
error = len;
}
kfree(mem);
return error;
}
/bio.c
0,0 → 1,225
/*
* linux/fs/nfs/bio.c
*
* Block I/O for NFS
*
* Partial copy of Linus' read cache modifications to fs/nfs/file.c
* modified for async RPC by okir@monad.swb.de
*
* We do an ugly hack here in order to return proper error codes to the
* user program when a read request failed. This is a huge problem because
* generic_file_read only checks the return value of inode->i_op->readpage()
* which is usually 0 for async RPC. To overcome this obstacle, we set
* the error bit of the page to 1 when an error occurs, and make nfs_readpage
* transmit requests synchronously when encountering this.
*
* Another possible solution to this problem may be to have a cache of recent
* RPC call results indexed by page pointer, or even a result code field
* in struct page.
*
* June 96: Added retries of RPCs that seem to have failed for a transient
* reason.
*/
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/nfs_fs.h>
#include <linux/nfsiod.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
#undef DEBUG_BIO
#ifdef DEBUG_BIO
#define dprintk(args...) printk(## args)
#else
#define dprintk(args...) /* nothing */
#endif
 
static inline int
do_read_nfs_sync(struct inode * inode, struct page * page)
{
struct nfs_fattr fattr;
int result, refresh = 0;
int count = PAGE_SIZE;
int rsize = NFS_SERVER(inode)->rsize;
char *buf = (char *) page_address(page);
unsigned long pos = page->offset;
 
dprintk("NFS: do_read_nfs_sync(%p)\n", page);
 
set_bit(PG_locked, &page->flags);
clear_bit(PG_error, &page->flags);
 
do {
if (count < rsize)
rsize = count;
result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
pos, rsize, buf, &fattr);
dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n",
NFS_SERVER(inode)->hostname,
inode->i_dev, inode->i_ino,
pos, rsize, buf, result);
/*
* Even if we had a partial success we can't mark the page
* cache valid.
*/
if (result < 0)
goto io_error;
refresh = 1;
count -= result;
pos += result;
buf += result;
if (result < rsize)
break;
} while (count);
 
memset(buf, 0, count);
set_bit(PG_uptodate, &page->flags);
result = 0;
 
io_error:
if (refresh)
nfs_refresh_inode(inode, &fattr);
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
return result;
}
 
/*
* This is the function to (re-) transmit an NFS readahead request
*/
static int
nfsiod_read_setup(struct nfsiod_req *req)
{
struct inode *inode = req->rq_inode;
struct page *page = req->rq_page;
 
return nfs_proc_read_request(&req->rq_rpcreq,
NFS_SERVER(inode), NFS_FH(inode),
page->offset, PAGE_SIZE,
(__u32 *) page_address(page));
}
 
/*
* This is the callback from nfsiod telling us whether a reply was
* received or some error occurred (timeout or socket shutdown).
*/
static int
nfsiod_read_result(int result, struct nfsiod_req *req)
{
struct nfs_server *server = NFS_SERVER(req->rq_inode);
struct page *page = req->rq_page;
static int succ = 0, fail = 0;
int i;
 
dprintk("BIO: received callback for page %p, result %d\n",
page, result);
 
if (result >= 0) {
struct nfs_fattr fattr;
 
result = nfs_proc_read_reply(&req->rq_rpcreq, &fattr);
if (result >= 0) {
nfs_refresh_inode(req->rq_inode, &fattr);
if (result < PAGE_SIZE)
memset((u8 *) page_address(page)+result,
0, PAGE_SIZE-result);
}
} else
if (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)) {
/* XXX: Theoretically, we'd have to increment the initial
* timeo here; but I'm not going to bother with this now
* because this old nfsiod stuff will soon die anyway.
*/
result = -EAGAIN;
}
 
if (result == -EAGAIN && req->rq_retries--) {
dprintk("BIO: retransmitting request.\n");
memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
while (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0)
schedule();
current->fsuid = req->rq_fsuid;
current->fsgid = req->rq_fsgid;
for (i = 0; i < NGROUPS; i++)
current->groups[i] = req->rq_groups[i];
nfsiod_read_setup(req);
return 0;
}
if (result >= 0) {
set_bit(PG_uptodate, &page->flags);
succ++;
} else {
dprintk("BIO: %d successful reads, %d failures\n", succ, fail);
set_bit(PG_error, &page->flags);
fail++;
}
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
free_page(page_address(page));
return 1;
}
 
static inline int
do_read_nfs_async(struct inode *inode, struct page *page)
{
struct nfsiod_req *req;
int result, i;
 
dprintk("NFS: do_read_nfs_async(%p)\n", page);
 
set_bit(PG_locked, &page->flags);
clear_bit(PG_error, &page->flags);
 
if (!(req = nfsiod_reserve(NFS_SERVER(inode))))
return -EAGAIN;
 
req->rq_retries = 5;
req->rq_callback = nfsiod_read_result;
req->rq_inode = inode;
req->rq_page = page;
 
req->rq_fsuid = current->fsuid;
req->rq_fsgid = current->fsgid;
for (i = 0; i < NGROUPS; i++)
req->rq_groups[i] = current->groups[i];
 
if ((result = nfsiod_read_setup(req)) >= 0) {
page->count++;
nfsiod_enqueue(req);
} else {
dprintk("NFS: deferring async READ request.\n");
nfsiod_release(req);
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
}
 
return result < 0? result : 0;
}
 
int
nfs_readpage(struct inode *inode, struct page *page)
{
unsigned long address;
int error = -1;
 
/* In case we're called from a page fault we want to */
/* make sure we're runnable before we schedule.. */
current->state = TASK_RUNNING;
dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
address = page_address(page);
page->count++;
if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
error = do_read_nfs_async(inode, page);
if (error < 0) /* couldn't enqueue */
error = do_read_nfs_sync(inode, page);
free_page(address);
return error;
}
/nfsroot.c
0,0 → 1,1724
/*
* linux/fs/nfs/nfsroot.c -- version 2.3
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
*
* For parts of this file:
* Copyright (C) 1996 Martin Mares <mj@k332.feld.cvut.cz>
*
* Allow an NFS filesystem to be mounted as root. The way this works is:
* (1) Determine the local IP address via RARP or BOOTP or from the
* kernel command line.
* (2) Handle RPC negotiation with the system which replied to RARP or
* was reported as a boot server by BOOTP or manually.
* (3) The actual mounting is done later, when init() is running.
*
*
* Changes:
*
* R. Drahtmueller : Set IFF_MULTICAST in dev->flags if applicable.
* Alan Cox : Removed get_address name clash with FPU.
* Alan Cox : Reformatted a bit.
* Gero Kuhlmann : Code cleanup
* Michael Rausch : Fixed recognition of an incoming RARP answer.
* Martin Mares : (2.0) Auto-configuration via BOOTP supported.
* Martin Mares : Manual selection of interface & BOOTP/RARP.
* Martin Mares : Using network routes instead of host routes,
* allowing the default configuration to be used
* for normal operation of the host.
* Martin Mares : Randomized timer with exponential backoff
* installed to minimize network congestion.
* Martin Mares : Code cleanup.
* Martin Mares : (2.1) BOOTP and RARP made configuration options.
* Martin Mares : Server hostname generation fixed.
* Gerd Knorr : Fixed wired inode handling
* Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored.
* Martin Mares : RARP replies not tested for server address.
* Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please
* send me your new patches _before_ bothering
* Linus so that I don' always have to cleanup
* _afterwards_ - thanks)
* Gero Kuhlmann : Last changes of Martin Mares undone.
* Gero Kuhlmann : RARP replies are tested for specified server
* again. However, it's now possible to have
* different RARP and NFS servers.
* Gero Kuhlmann : "0.0.0.0" addresses from command line are
* now mapped to INADDR_NONE.
* Gero Kuhlmann : Fixed a bug which prevented BOOTP path name
* from being used (thanks to Leo Spiekman)
* Andy Walker : Allow to specify the NFS server in nfs_root
* without giving a path name
* Swen Th=FCmmler : Allow to specify the NFS options in nfs_root
* without giving a path name. Fix BOOTP request
* for domainname (domainname is NIS domain, not
* DNS domain!). Skip dummy devices for BOOTP.
* Jacek Zapala : Fixed a bug which prevented server-ip address
* from nfsroot parameter from being used.
*
*/
 
 
/* Define this to allow debugging output */
#undef NFSROOT_DEBUG
#undef NFSROOT_BOOTP_DEBUG
 
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/random.h>
#include <linux/fcntl.h>
 
#include <asm/param.h>
#include <linux/utsname.h>
#include <linux/in.h>
#include <linux/if.h>
#include <linux/inet.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#include <net/ax25.h> /* For AX25_P_IP */
#endif
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/route.h>
#include <linux/nfs.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/in.h>
#include <net/route.h>
#include <net/sock.h>
 
#include <asm/segment.h>
 
/* Range of privileged ports */
#define STARTPORT 600
#define ENDPORT 1023
#define NPORTS (ENDPORT - STARTPORT + 1)
 
 
/* Define the timeout for waiting for a RARP/BOOTP reply */
#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
#define CONF_RETRIES 10 /* 10 retries */
#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */
#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
 
 
/* List of open devices */
struct open_dev {
struct device *dev;
unsigned short old_flags;
struct open_dev *next;
};
 
static struct open_dev *open_base = NULL;
 
 
/* IP configuration */
static struct device *root_dev = NULL; /* Device selected for booting */
static char user_dev_name[IFNAMSIZ]; /* Name of user-selected boot device */
static struct sockaddr_in myaddr; /* My IP address */
static struct sockaddr_in server; /* Server IP address */
static struct sockaddr_in gateway; /* Gateway IP address */
static struct sockaddr_in netmask; /* Netmask for local subnet */
 
 
/* BOOTP/RARP variables */
static int bootp_flag; /* User said: Use BOOTP! */
static int rarp_flag; /* User said: Use RARP! */
static int bootp_dev_count = 0; /* Number of devices allowing BOOTP */
static int rarp_dev_count = 0; /* Number of devices allowing RARP */
static struct sockaddr_in rarp_serv; /* IP address of RARP server */
 
#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
static volatile int pkt_arrived; /* BOOTP/RARP packet detected */
 
#define ARRIVED_BOOTP 1
#define ARRIVED_RARP 2
#endif
 
 
/* NFS-related data */
static struct nfs_mount_data nfs_data; /* NFS mount info */
static char nfs_path[NFS_MAXPATHLEN] = ""; /* Name of directory to mount */
static int nfs_port; /* Port to connect to for NFS */
 
 
/* Yes, we use sys_socket, but there's no include file for it */
extern asmlinkage int sys_socket(int family, int type, int protocol);
 
 
 
/***************************************************************************
 
Device Handling Subroutines
 
***************************************************************************/
 
/*
* Setup and initialize all network devices. If there is a user-preferred
* interface, ignore all other interfaces.
*/
static int root_dev_open(void)
{
struct open_dev *openp, **last;
struct device *dev;
unsigned short old_flags;
 
last = &open_base;
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->type < ARPHRD_SLIP &&
dev->family == AF_INET &&
!(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
(0 != strncmp(dev->name, "dummy", 5)) &&
(!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
/* First up the interface */
old_flags = dev->flags;
#ifdef CONFIG_IP_MULTICAST
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
#else
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
#endif
if (!(old_flags & IFF_UP) && dev_open(dev)) {
dev->flags = old_flags;
continue;
}
openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
GFP_ATOMIC);
if (openp == NULL)
continue;
openp->dev = dev;
openp->old_flags = old_flags;
*last = openp;
last = &openp->next;
bootp_dev_count++;
if (!(dev->flags & IFF_NOARP))
rarp_dev_count++;
#ifdef NFSROOT_DEBUG
printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
#endif
}
}
*last = NULL;
 
if (!bootp_dev_count && !rarp_dev_count) {
printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
return -1;
}
return 0;
}
 
 
/*
* Restore the state of all devices. However, keep the root device open
* for the upcoming mount.
*/
static void root_dev_close(void)
{
struct open_dev *openp;
struct open_dev *nextp;
 
openp = open_base;
while (openp != NULL) {
nextp = openp->next;
openp->next = NULL;
if (openp->dev != root_dev) {
if (!(openp->old_flags & IFF_UP))
dev_close(openp->dev);
openp->dev->flags = openp->old_flags;
}
kfree_s(openp, sizeof(struct open_dev));
openp = nextp;
}
}
 
 
 
/***************************************************************************
 
RARP Subroutines
 
***************************************************************************/
 
#ifdef CONFIG_RNFS_RARP
 
extern void arp_send(int type, int ptype, unsigned long target_ip,
struct device *dev, unsigned long src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw);
 
static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
struct packet_type *pt);
 
 
static struct packet_type rarp_packet_type = {
0, /* Should be: __constant_htons(ETH_P_RARP)
* - but this _doesn't_ come out constant! */
NULL, /* Listen to all devices */
root_rarp_recv,
NULL,
NULL
};
 
 
/*
* Register the packet type for RARP
*/
static void root_rarp_open(void)
{
rarp_packet_type.type = htons(ETH_P_RARP);
dev_add_pack(&rarp_packet_type);
}
 
 
/*
* Deregister the RARP packet type
*/
static void root_rarp_close(void)
{
rarp_packet_type.type = htons(ETH_P_RARP);
dev_remove_pack(&rarp_packet_type);
}
 
 
/*
* Receive RARP packets.
*/
static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
struct arphdr *rarp = (struct arphdr *)skb->h.raw;
unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
unsigned long sip, tip;
unsigned char *sha, *tha; /* s for "source", t for "target" */
 
/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
kfree_skb(skb, FREE_READ);
return 0;
}
 
/* If it's not a RARP reply, delete it. */
if (rarp->ar_op != htons(ARPOP_RREPLY)) {
kfree_skb(skb, FREE_READ);
return 0;
}
 
/* If it's not ethernet or AX25, delete it. */
if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
#endif
rarp->ar_pln != 4) {
kfree_skb(skb, FREE_READ);
return 0;
}
 
/* Extract variable width fields */
sha = rarp_ptr;
rarp_ptr += dev->addr_len;
memcpy(&sip, rarp_ptr, 4);
rarp_ptr += 4;
tha = rarp_ptr;
rarp_ptr += dev->addr_len;
memcpy(&tip, rarp_ptr, 4);
 
/* Discard packets which are not meant for us. */
if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
kfree_skb(skb, FREE_READ);
return 0;
}
/* Discard packets which are not from specified server. */
if (rarp_flag && !bootp_flag &&
rarp_serv.sin_addr.s_addr != INADDR_NONE &&
rarp_serv.sin_addr.s_addr != sip) {
kfree_skb(skb, FREE_READ);
return 0;
}
 
/*
* The packet is what we were looking for. Setup the global
* variables.
*/
cli();
if (pkt_arrived) {
sti();
kfree_skb(skb, FREE_READ);
return 0;
}
pkt_arrived = ARRIVED_RARP;
sti();
root_dev = dev;
 
if (myaddr.sin_addr.s_addr == INADDR_NONE) {
myaddr.sin_family = dev->family;
myaddr.sin_addr.s_addr = tip;
}
if (server.sin_addr.s_addr == INADDR_NONE) {
server.sin_family = dev->family;
server.sin_addr.s_addr = sip;
}
kfree_skb(skb, FREE_READ);
return 0;
}
 
 
/*
* Send RARP request packet over all devices which allow RARP.
*/
static void root_rarp_send(void)
{
struct open_dev *openp;
struct device *dev;
int num = 0;
 
for (openp = open_base; openp != NULL; openp = openp->next) {
dev = openp->dev;
if (!(dev->flags & IFF_NOARP)) {
arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
dev->dev_addr, dev->dev_addr);
num++;
}
}
}
#endif
 
 
 
/***************************************************************************
 
BOOTP Subroutines
 
***************************************************************************/
 
#ifdef CONFIG_RNFS_BOOTP
 
static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
 
static int bootp_xmit_fd = -1; /* Socket descriptor for transmit */
static struct socket *bootp_xmit_sock; /* The socket itself */
static int bootp_recv_fd = -1; /* Socket descriptor for receive */
static struct socket *bootp_recv_sock; /* The socket itself */
 
struct bootp_pkt { /* BOOTP packet format */
u8 op; /* 1=request, 2=reply */
u8 htype; /* HW address type */
u8 hlen; /* HW address length */
u8 hops; /* Used only by gateways */
u32 xid; /* Transaction ID */
u16 secs; /* Seconds since we started */
u16 flags; /* Just what is says */
u32 client_ip; /* Client's IP address if known */
u32 your_ip; /* Assigned IP address */
u32 server_ip; /* Server's IP address */
u32 relay_ip; /* IP address of BOOTP relay */
u8 hw_addr[16]; /* Client's HW address */
u8 serv_name[64]; /* Server host name */
u8 boot_file[128]; /* Name of boot file */
u8 vendor_area[128]; /* Area for extensions */
};
 
#define BOOTP_REQUEST 1
#define BOOTP_REPLY 2
 
static struct bootp_pkt *xmit_bootp; /* Packet being transmitted */
static struct bootp_pkt *recv_bootp; /* Packet being received */
 
static int bootp_have_route = 0; /* BOOTP route installed */
 
 
/*
* Free BOOTP packet buffers
*/
static void root_free_bootp(void)
{
if (xmit_bootp) {
kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
xmit_bootp = NULL;
}
if (recv_bootp) {
kfree_s(recv_bootp, sizeof(struct bootp_pkt));
recv_bootp = NULL;
}
}
 
 
/*
* Allocate memory for BOOTP packet buffers
*/
static inline int root_alloc_bootp(void)
{
if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
!(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
printk("BOOTP: Out of memory!");
return -1;
}
return 0;
}
 
 
/*
* Create default route for BOOTP sending
*/
static int root_add_bootp_route(void)
{
struct rtentry route;
 
memset(&route, 0, sizeof(route));
route.rt_dev = bootp_dev->name;
route.rt_mss = bootp_dev->mtu;
route.rt_flags = RTF_UP;
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
if (ip_rt_new(&route)) {
printk(KERN_ERR "BOOTP: Adding of route failed!\n");
return -1;
}
bootp_have_route = 1;
return 0;
}
 
 
/*
* Delete default route for BOOTP sending
*/
static int root_del_bootp_route(void)
{
struct rtentry route;
 
if (!bootp_have_route)
return 0;
memset(&route, 0, sizeof(route));
((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
if (ip_rt_kill(&route)) {
printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
return -1;
}
bootp_have_route = 0;
return 0;
}
 
 
/*
* Open UDP socket.
*/
static int root_open_udp_sock(int *fd, struct socket **sock)
{
struct file *file;
struct inode *inode;
 
*fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (*fd >= 0) {
file = current->files->fd[*fd];
inode = file->f_inode;
*sock = &inode->u.socket_i;
return 0;
}
 
printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
return -1;
}
 
 
/*
* Connect UDP socket.
*/
static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
{
struct sockaddr_in sa;
int result;
 
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(addr);
sa.sin_port = htons(port);
result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
if (result < 0) {
printk(KERN_ERR "BOOTP: connect() failed\n");
return -1;
}
return 0;
}
 
 
/*
* Bind UDP socket.
*/
static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
{
struct sockaddr_in sa;
int result;
 
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(addr);
sa.sin_port = htons(port);
result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
if (result < 0) {
printk(KERN_ERR "BOOTP: bind() failed\n");
return -1;
}
return 0;
}
 
 
/*
* Send UDP packet.
*/
static inline int root_send_udp(struct socket *sock, void *buf, int size)
{
u32 oldfs;
int result;
struct msghdr msg;
struct iovec iov;
 
oldfs = get_fs();
set_fs(get_ds());
iov.iov_base = buf;
iov.iov_len = size;
msg.msg_name = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
set_fs(oldfs);
return (result != size);
}
 
 
/*
* Try to receive UDP packet.
*/
static inline int root_recv_udp(struct socket *sock, void *buf, int size)
{
u32 oldfs;
int result;
struct msghdr msg;
struct iovec iov;
 
oldfs = get_fs();
set_fs(get_ds());
iov.iov_base = buf;
iov.iov_len = size;
msg.msg_name = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_namelen = 0;
result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
set_fs(oldfs);
return result;
}
 
 
/*
* Initialize BOOTP extension fields in the request.
*/
static void root_bootp_init_ext(u8 *e)
{
*e++ = 99; /* RFC1048 Magic Cookie */
*e++ = 130;
*e++ = 83;
*e++ = 99;
*e++ = 1; /* Subnet mask request */
*e++ = 4;
e += 4;
*e++ = 3; /* Default gateway request */
*e++ = 4;
e += 4;
*e++ = 12; /* Host name request */
*e++ = 32;
e += 32;
*e++ = 40; /* NIS Domain name request */
*e++ = 32;
e += 32;
*e++ = 17; /* Boot path */
*e++ = 32;
e += 32;
*e = 255; /* End of the list */
}
 
 
/*
* Deinitialize the BOOTP mechanism.
*/
static void root_bootp_close(void)
{
if (bootp_xmit_fd != -1)
sys_close(bootp_xmit_fd);
if (bootp_recv_fd != -1)
sys_close(bootp_recv_fd);
root_del_bootp_route();
root_free_bootp();
}
 
 
/*
* Initialize the BOOTP mechanism.
*/
static int root_bootp_open(void)
{
struct open_dev *openp;
struct device *dev, *best_dev;
 
/*
* Select the best interface for BOOTP. We try to select a first
* Ethernet-like interface. It's shame I know no simple way how to send
* BOOTP's to all interfaces, but it doesn't apply to usual diskless
* stations as they don't have multiple interfaces.
*/
 
best_dev = NULL;
for (openp = open_base; openp != NULL; openp = openp->next) {
dev = openp->dev;
if (dev->flags & IFF_BROADCAST) {
if (!best_dev ||
((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
best_dev = dev;
}
}
 
if (!best_dev) {
printk(KERN_ERR "BOOTP: This cannot happen!\n");
return -1;
}
bootp_dev = best_dev;
 
/* Allocate memory for BOOTP packets */
if (root_alloc_bootp())
return -1;
 
/* Construct BOOTP request */
memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
xmit_bootp->op = BOOTP_REQUEST;
get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
xmit_bootp->htype = best_dev->type;
xmit_bootp->hlen = best_dev->addr_len;
memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
root_bootp_init_ext(xmit_bootp->vendor_area);
#ifdef NFSROOT_BOOTP_DEBUG
{
int x;
printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
xmit_bootp->xid,
best_dev->name,
xmit_bootp->htype,
xmit_bootp->hlen);
for(x=0; x<xmit_bootp->hlen; x++)
printk("%02x", xmit_bootp->hw_addr[x]);
printk("\n");
}
#endif
 
/* Create default route to that interface */
if (root_add_bootp_route())
return -1;
 
/* Open the sockets */
if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
return -1;
 
/* Bind/connect the sockets */
((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
((struct sock *) bootp_recv_sock->data) -> reuse = 1;
if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
return -1;
 
return 0;
}
 
 
/*
* Send BOOTP request.
*/
static int root_bootp_send(u32 jiffies)
{
xmit_bootp->secs = htons(jiffies / HZ);
return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
}
 
 
/*
* Copy BOOTP-supplied string if not already set.
*/
static int root_bootp_string(char *dest, char *src, int len, int max)
{
if (*dest || !len)
return 0;
if (len > max-1)
len = max-1;
strncpy(dest, src, len);
dest[len] = '\0';
return 1;
}
 
 
/*
* Process BOOTP extension.
*/
static void root_do_bootp_ext(u8 *ext)
{
#ifdef NFSROOT_BOOTP_DEBUG
u8 *c;
 
printk("BOOTP: Got extension %02x",*ext);
for(c=ext+2; c<ext+2+ext[1]; c++)
printk(" %02x", *c);
printk("\n");
#endif
 
switch (*ext++) {
case 1: /* Subnet mask */
if (netmask.sin_addr.s_addr == INADDR_NONE)
memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
break;
case 3: /* Default gateway */
if (gateway.sin_addr.s_addr == INADDR_NONE)
memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
break;
case 12: /* Host name */
root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
break;
case 40: /* NIS Domain name */
root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
break;
case 17: /* Root path */
root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
break;
}
}
 
 
/*
* Receive BOOTP request.
*/
static void root_bootp_recv(void)
{
int len;
u8 *ext, *end, *opt;
 
len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
if (len < 0)
return;
 
/* Check consistency of incoming packet */
if (len < 300 || /* See RFC 1542:2.1 */
recv_bootp->op != BOOTP_REPLY ||
recv_bootp->htype != xmit_bootp->htype ||
recv_bootp->hlen != xmit_bootp->hlen ||
recv_bootp->xid != xmit_bootp->xid) {
#ifdef NFSROOT_BOOTP_DEBUG
printk("?");
#endif
return;
}
 
/* Record BOOTP packet arrival in the global variables */
cli();
if (pkt_arrived) {
sti();
return;
}
pkt_arrived = ARRIVED_BOOTP;
sti();
root_dev = bootp_dev;
 
/* Extract basic fields */
myaddr.sin_addr.s_addr = recv_bootp->your_ip;
if (server.sin_addr.s_addr==INADDR_NONE)
server.sin_addr.s_addr = recv_bootp->server_ip;
 
/* Parse extensions */
if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */
recv_bootp->vendor_area[1] == 130 &&
recv_bootp->vendor_area[2] == 83 &&
recv_bootp->vendor_area[3] == 99) {
ext = &recv_bootp->vendor_area[4];
end = (u8 *) recv_bootp + len;
while (ext < end && *ext != 255) {
if (*ext == 0) /* Padding */
ext++;
else {
opt = ext;
ext += ext[1] + 2;
if (ext <= end)
root_do_bootp_ext(opt);
}
}
}
}
#endif
 
 
 
/***************************************************************************
 
Dynamic configuration of IP.
 
***************************************************************************/
 
#ifdef CONFIG_RNFS_DYNAMIC
 
/*
* Determine client and server IP numbers and appropriate device by using
* the RARP and BOOTP protocols.
*/
static int root_auto_config(void)
{
int retries;
unsigned long timeout, jiff;
unsigned long start_jiffies;
 
/*
* If neither BOOTP nor RARP was selected, return with an error. This
* routine gets only called when some pieces of information are mis-
* sing, and without BOOTP and RARP we are not able to get that in-
* formation.
*/
if (!bootp_flag && !rarp_flag) {
printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
return -1;
}
 
#ifdef CONFIG_RNFS_BOOTP
if (bootp_flag && !bootp_dev_count) {
printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
bootp_flag = 0;
}
#else
bootp_flag = 0;
#endif
 
#ifdef CONFIG_RNFS_RARP
if (rarp_flag && !rarp_dev_count) {
printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
rarp_flag = 0;
}
#else
rarp_flag = 0;
#endif
 
if (!bootp_flag && !rarp_flag)
/* Error message already printed */
return -1;
 
/*
* Setup RARP and BOOTP protocols
*/
#ifdef CONFIG_RNFS_RARP
if (rarp_flag)
root_rarp_open();
#endif
#ifdef CONFIG_RNFS_BOOTP
if (bootp_flag && root_bootp_open() < 0) {
root_bootp_close();
return -1;
}
#endif
 
/*
* Send requests and wait, until we get an answer. This loop
* seems to be a terrible waste of CPU time, but actually there is
* only one process running at all, so we don't need to use any
* scheduler functions.
* [Actually we could now, but the nothing else running note still
* applies.. - AC]
*/
printk(KERN_NOTICE "Sending %s%s%s requests...",
bootp_flag ? "BOOTP" : "",
bootp_flag && rarp_flag ? " and " : "",
rarp_flag ? "RARP" : "");
start_jiffies = jiffies;
retries = CONF_RETRIES;
get_random_bytes(&timeout, sizeof(timeout));
timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
for(;;) {
#ifdef CONFIG_RNFS_BOOTP
if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
printk(" BOOTP failed!\n");
root_bootp_close();
bootp_flag = 0;
if (!rarp_flag)
break;
}
#endif
#ifdef CONFIG_RNFS_RARP
if (rarp_flag)
root_rarp_send();
#endif
printk(".");
jiff = jiffies + timeout;
while (jiffies < jiff && !pkt_arrived)
#ifdef CONFIG_RNFS_BOOTP
root_bootp_recv();
#else
;
#endif
if (pkt_arrived)
break;
if (! --retries) {
printk(" timed out!\n");
break;
}
timeout = timeout CONF_TIMEOUT_MULT;
if (timeout > CONF_TIMEOUT_MAX)
timeout = CONF_TIMEOUT_MAX;
}
 
#ifdef CONFIG_RNFS_RARP
if (rarp_flag)
root_rarp_close();
#endif
#ifdef CONFIG_RNFS_BOOTP
if (bootp_flag)
root_bootp_close();
#endif
 
if (!pkt_arrived)
return -1;
 
printk(" OK\n");
printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
(pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
in_ntoa(server.sin_addr.s_addr));
printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
 
return 0;
}
#endif
 
 
 
/***************************************************************************
 
Parsing of options
 
***************************************************************************/
 
 
/*
* The following integer options are recognized
*/
static struct nfs_int_opts {
char *name;
int *val;
} root_int_opts[] = {
{ "port", &nfs_port },
{ "rsize", &nfs_data.rsize },
{ "wsize", &nfs_data.wsize },
{ "timeo", &nfs_data.timeo },
{ "retrans", &nfs_data.retrans },
{ "acregmin", &nfs_data.acregmin },
{ "acregmax", &nfs_data.acregmax },
{ "acdirmin", &nfs_data.acdirmin },
{ "acdirmax", &nfs_data.acdirmax },
{ NULL, NULL }
};
 
 
/*
* And now the flag options
*/
static struct nfs_bool_opts {
char *name;
int and_mask;
int or_mask;
} root_bool_opts[] = {
{ "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
{ "hard", ~NFS_MOUNT_SOFT, 0 },
{ "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
{ "nointr", ~NFS_MOUNT_INTR, 0 },
{ "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
{ "noposix", ~NFS_MOUNT_POSIX, 0 },
{ "cto", ~NFS_MOUNT_NOCTO, 0 },
{ "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
{ "ac", ~NFS_MOUNT_NOAC, 0 },
{ "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
{ NULL, 0, 0 }
};
 
 
/*
* Prepare the NFS data structure and parse any options. This tries to
* set as many values in the nfs_data structure as known right now.
*/
static int root_nfs_name(char *name)
{
char buf[NFS_MAXPATHLEN];
char *cp, *cq, *options, *val;
int octets = 0;
 
/* It is possible to override the server IP number here */
cp = cq = name;
while (octets < 4) {
while (*cp >= '0' && *cp <= '9')
cp++;
if (cp == cq || cp - cq > 3)
break;
if (*cp == '.' || octets == 3)
octets++;
if (octets < 4)
cp++;
cq = cp;
}
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
if (*cp == ':')
*cp++ = '\0';
server.sin_addr.s_addr = in_aton(name);
name = cp;
}
 
/* Clear the nfs_data structure and setup the server hostname */
memset(&nfs_data, 0, sizeof(nfs_data));
strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
sizeof(nfs_data.hostname)-1);
 
/* Set the name of the directory to mount */
if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
strncpy(buf, name, NFS_MAXPATHLEN);
else
strncpy(buf, nfs_path, NFS_MAXPATHLEN);
if ((options = strchr(buf, ',')))
*options++ = '\0';
if (!strcmp(buf, "default"))
strcpy(buf, NFS_ROOT);
cp = in_ntoa(myaddr.sin_addr.s_addr);
if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
return -1;
}
/* update nfs_path with path from nfsroot=... command line parameter */
if (*buf)
sprintf(nfs_path, buf, cp);
 
/* Set some default values */
nfs_port = -1;
nfs_data.version = NFS_MOUNT_VERSION;
nfs_data.flags = 0;
nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
nfs_data.timeo = 7;
nfs_data.retrans = 3;
nfs_data.acregmin = 3;
nfs_data.acregmax = 60;
nfs_data.acdirmin = 30;
nfs_data.acdirmax = 60;
 
/* Process any options */
if (options) {
cp = strtok(options, ",");
while (cp) {
if ((val = strchr(cp, '='))) {
struct nfs_int_opts *opts = root_int_opts;
*val++ = '\0';
while (opts->name && strcmp(opts->name, cp))
opts++;
if (opts->name)
*(opts->val) = (int) simple_strtoul(val, NULL, 10);
} else {
struct nfs_bool_opts *opts = root_bool_opts;
while (opts->name && strcmp(opts->name, cp))
opts++;
if (opts->name) {
nfs_data.flags &= opts->and_mask;
nfs_data.flags |= opts->or_mask;
}
}
cp = strtok(NULL, ",");
}
}
return 0;
}
 
 
/*
* Tell the user what's going on.
*/
#ifdef NFSROOT_DEBUG
static void root_nfs_print(void)
{
#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
 
printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
root_dev ? root_dev->name : "none");
printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
printk("host=%s, domain=%s\n",
system_utsname.nodename[0] ? system_utsname.nodename : "none",
system_utsname.domainname[0] ? system_utsname.domainname : "none");
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
nfs_path, nfs_data.hostname);
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
nfs_data.acregmin, nfs_data.acregmax,
nfs_data.acdirmin, nfs_data.acdirmax);
printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n",
nfs_port, nfs_data.flags);
 
#undef IN_NTOA
}
#endif
 
 
/*
* Decode any IP configuration options in the "nfsaddrs" kernel command
* line parameter. It consists of option fields separated by colons in
* the following order:
*
* <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
*
* Any of the fields can be empty which means to use a default value:
* <client-ip> - address given by BOOTP or RARP
* <server-ip> - address of host returning BOOTP or RARP packet
* <gw-ip> - none, or the address returned by BOOTP
* <netmask> - automatically determined from <client-ip>, or the
* one returned by BOOTP
* <host name> - <client-ip> in ASCII notation, or the name returned
* by BOOTP
* <device> - use all available devices for RARP and the first
* one for BOOTP
* <bootp|rarp> - use both protocols to determine my own address
*/
static void root_nfs_addrs(char *addrs)
{
char *cp, *ip, *dp;
int num = 0;
 
/* Clear all addresses and strings */
myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
gateway.sin_family = netmask.sin_family = AF_INET;
myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
system_utsname.nodename[0] = '\0';
system_utsname.domainname[0] = '\0';
user_dev_name[0] = '\0';
bootp_flag = rarp_flag = 1;
 
/* The following is just a shortcut for automatic IP configuration */
if (!strcmp(addrs, "bootp")) {
rarp_flag = 0;
return;
} else if (!strcmp(addrs, "rarp")) {
bootp_flag = 0;
return;
} else if (!strcmp(addrs, "both")) {
return;
}
 
/* Parse the whole string */
ip = addrs;
while (ip && *ip) {
if ((cp = strchr(ip, ':')))
*cp++ = '\0';
if (strlen(ip) > 0) {
#ifdef NFSROOT_DEBUG
printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
num, ip);
#endif
switch (num) {
case 0:
if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
myaddr.sin_addr.s_addr = INADDR_NONE;
break;
case 1:
if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
server.sin_addr.s_addr = INADDR_NONE;
break;
case 2:
if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
gateway.sin_addr.s_addr = INADDR_NONE;
break;
case 3:
if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
netmask.sin_addr.s_addr = INADDR_NONE;
break;
case 4:
if ((dp = strchr(ip, '.'))) {
*dp++ = '\0';
strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
system_utsname.domainname[__NEW_UTS_LEN] = '\0';
}
strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
break;
case 5:
strncpy(user_dev_name, ip, IFNAMSIZ);
user_dev_name[IFNAMSIZ-1] = '\0';
break;
case 6:
if (!strcmp(ip, "rarp"))
bootp_flag = 0;
else if (!strcmp(ip, "bootp"))
rarp_flag = 0;
else if (strcmp(ip, "both"))
bootp_flag = rarp_flag = 0;
break;
default:
break;
}
}
ip = cp;
num++;
}
rarp_serv = server;
}
 
 
/*
* Set the interface address and configure a route to the server.
*/
static int root_nfs_setup(void)
{
struct rtentry route;
 
/* Set the default system name in case none was previously found */
if (!system_utsname.nodename[0]) {
strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
}
 
/* Set the correct netmask */
if (netmask.sin_addr.s_addr == INADDR_NONE)
netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
 
/* Setup the device correctly */
root_dev->family = myaddr.sin_family;
root_dev->pa_addr = myaddr.sin_addr.s_addr;
root_dev->pa_mask = netmask.sin_addr.s_addr;
root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
root_dev->pa_dstaddr = 0;
 
/*
* Now add a route to the server. If there is no gateway given,
* the server is on the same subnet, so we establish only a route to
* the local network. Otherwise we create a route to the gateway (the
* same local network router as in the former case) and then setup a
* gatewayed default route. Note that this gives sufficient network
* setup even for full system operation in all common cases.
*/
memset(&route, 0, sizeof(route)); /* Local subnet route */
route.rt_dev = root_dev->name;
route.rt_mss = root_dev->mtu;
route.rt_flags = RTF_UP;
*((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
*((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
if (ip_rt_new(&route)) {
printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
return -1;
}
 
if (gateway.sin_addr.s_addr != INADDR_NONE) { /* Default route */
(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
(((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
*((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
route.rt_flags |= RTF_GATEWAY;
if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
return -1;
}
if (ip_rt_new(&route)) {
printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
return -1;
}
} else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
return -1;
}
 
return 0;
}
 
 
/*
* Get the necessary IP addresses and prepare for mounting the required
* NFS filesystem.
*/
int nfs_root_init(char *nfsname, char *nfsaddrs)
{
/*
* Decode IP addresses and other configuration info contained
* in the nfsaddrs string (which came from the kernel command
* line).
*/
root_nfs_addrs(nfsaddrs);
 
/*
* Setup all network devices
*/
if (root_dev_open() < 0)
return -1;
 
/*
* If the config information is insufficient (e.g., our IP address or
* IP address of the boot server is missing or we have multiple network
* interfaces and no default was set), use BOOTP or RARP to get the
* missing values.
*
* Note that we don't try to set up correct routes for multiple
* interfaces (could be solved by trying icmp echo requests), because
* it's only necessary in the rare case of multiple ethernet devices
* in the (diskless) system and if the server is on another subnet.
* If only one interface is installed, the routing is obvious.
*/
if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
server.sin_addr.s_addr == INADDR_NONE ||
(open_base != NULL && open_base->next != NULL))
#ifdef CONFIG_RNFS_DYNAMIC
&& root_auto_config() < 0
#endif
) {
root_dev_close();
return -1;
}
if (root_dev == NULL) {
if (open_base != NULL && open_base->next == NULL) {
root_dev = open_base->dev;
} else {
printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
root_dev_close();
return -1;
}
}
 
/*
* Close all network devices except the device which connects to
* server
*/
root_dev_close();
 
/*
* Decode the root directory path name and NFS options from
* the kernel command line. This has to go here in order to
* be able to use the client IP address for the remote root
* directory (necessary for pure RARP booting).
*/
if (root_nfs_name(nfsname) < 0)
return -1;
 
/*
* Setup devices and routes. The server directory is actually
* mounted after init() has been started.
*/
if (root_nfs_setup() < 0)
return -1;
 
#ifdef NFSROOT_DEBUG
root_nfs_print();
#endif
 
return 0;
}
 
 
/***************************************************************************
 
Routines to actually mount the root directory
 
***************************************************************************/
 
static struct file *nfs_file; /* File descriptor pointing to inode */
static struct inode *nfs_sock_inode; /* Inode containing socket */
static int *rpc_packet = NULL; /* RPC packet */
 
 
/*
* Open a UDP socket.
*/
static int root_nfs_open(void)
{
/* Open the socket */
if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
return -1;
}
nfs_file = current->files->fd[nfs_data.fd];
nfs_sock_inode = nfs_file->f_inode;
return 0;
}
 
 
/*
* Close the UDP file descriptor. If nfs_read_super is successful, it
* increases the reference count, so we can simply close the file, and
* the socket keeps open.
*/
static void root_nfs_close(void)
{
/*
* The following close doesn't touch the server structure, which
* now contains a file pointer pointing into nowhere. The system
* _should_ crash as soon as someone tries to select on the root
* filesystem. Haven't tried it yet - we can still change it back
* to the old way of keeping a static copy of all important data
* structures, including their pointers. At least this should be
* checked out _carefully_ before going into a public release
* kernel. - GK
*/
sys_close(nfs_data.fd);
}
 
 
/*
* Find a suitable listening port and bind to it
*/
static int root_nfs_bind(void)
{
int res = -1;
short port = STARTPORT;
struct sockaddr_in *sin = &myaddr;
int i;
 
if (nfs_sock_inode->u.socket_i.ops->bind) {
for (i = 0; i < NPORTS && res < 0; i++) {
sin->sin_port = htons(port++);
if (port > ENDPORT) {
port = STARTPORT;
}
res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
(struct sockaddr *)sin,
sizeof(struct sockaddr_in));
}
}
if (res < 0) {
printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
root_nfs_close();
return -1;
}
#ifdef NFSROOT_DEBUG
printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
#endif
return 0;
}
 
 
/*
* Send an RPC request and wait for the answer
*/
static int *root_nfs_call(int *end)
{
struct socket *sock;
int dummylen;
static struct nfs_server s = {
0, /* struct file * */
0, /* struct rsock * */
{
0, "",
}, /* toaddr */
0, /* lock */
NULL, /* wait queue */
NFS_MOUNT_SOFT, /* flags */
0, 0, /* rsize, wsize */
0, /* timeo */
0, /* retrans */
3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
};
 
s.file = nfs_file;
sock = &((nfs_file->f_inode)->u.socket_i);
 
/* Extract the other end of the socket into s->toaddr */
sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
((struct sockaddr_in *) &s.toaddr)->sin_port = server.sin_port;
((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
 
s.rsock = rpc_makesock(nfs_file);
s.flags = nfs_data.flags;
s.rsize = nfs_data.rsize;
s.wsize = nfs_data.wsize;
s.timeo = nfs_data.timeo * HZ / 10;
s.retrans = nfs_data.retrans;
strcpy(s.hostname, nfs_data.hostname);
 
/*
* First connect the UDP socket to a server port, then send the
* packet out, and finally check whether the answer is OK.
*/
if (nfs_sock_inode->u.socket_i.ops->connect &&
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
(struct sockaddr *) &server,
sizeof(struct sockaddr_in),
nfs_file->f_flags) < 0)
return NULL;
if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
return NULL;
return rpc_verify(rpc_packet);
}
 
 
/*
* Create an RPC packet header
*/
static int *root_nfs_header(int proc, int program, int version)
{
int groups[] = { 0, NOGROUP };
 
if (rpc_packet == NULL) {
if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
return NULL;
}
}
return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
}
 
 
/*
* Query server portmapper for the port of a daemon program
*/
static int root_nfs_get_port(int program, int version)
{
int *p;
 
/* Prepare header for portmap request */
server.sin_port = htons(NFS_PMAP_PORT);
p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
if (!p)
return -1;
 
/* Set arguments for portmapper */
*p++ = htonl(program);
*p++ = htonl(version);
*p++ = htonl(IPPROTO_UDP);
*p++ = 0;
 
/* Send request to server portmapper */
if ((p = root_nfs_call(p)) == NULL)
return -1;
 
return ntohl(*p);
}
 
 
/*
* Get portnumbers for mountd and nfsd from server
*/
static int root_nfs_ports(void)
{
int port;
 
if (nfs_port < 0) {
if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
port = NFS_NFS_PORT;
}
nfs_port = port;
#ifdef NFSROOT_DEBUG
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
#endif
}
if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
port = NFS_MOUNT_PORT;
}
server.sin_port = htons(port);
#ifdef NFSROOT_DEBUG
printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
#endif
 
return 0;
}
 
 
/*
* Get a file handle from the server for the directory which is to be
* mounted
*/
static int root_nfs_get_handle(void)
{
int len, status, *p;
 
/* Prepare header for mountd request */
p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
if (!p) {
root_nfs_close();
return -1;
}
 
/* Set arguments for mountd */
len = strlen(nfs_path);
*p++ = htonl(len);
memcpy(p, nfs_path, len);
len = (len + 3) >> 2;
p[len] = 0;
p += len;
 
/* Send request to server mountd */
if ((p = root_nfs_call(p)) == NULL) {
root_nfs_close();
return -1;
}
status = ntohl(*p++);
if (status == 0) {
nfs_data.root = *((struct nfs_fh *) p);
printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
} else {
printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
status, nfs_path);
root_nfs_close();
return -1;
}
 
return 0;
}
 
 
/*
* Now actually mount the given directory
*/
static int root_nfs_do_mount(struct super_block *sb)
{
/* First connect to the nfsd port on the server */
server.sin_port = htons(nfs_port);
nfs_data.addr = server;
if (nfs_sock_inode->u.socket_i.ops->connect &&
nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
(struct sockaddr *) &server,
sizeof(struct sockaddr_in),
nfs_file->f_flags) < 0) {
root_nfs_close();
return -1;
}
 
/* Now (finally ;-)) read the super block for mounting */
if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
root_nfs_close();
return -1;
}
return 0;
}
 
 
/*
* Get the NFS port numbers and file handle, and then read the super-
* block for mounting.
*/
int nfs_root_mount(struct super_block *sb)
{
if (root_nfs_open() < 0)
return -1;
if (root_nfs_bind() < 0)
return -1;
if (root_nfs_ports() < 0)
return -1;
if (root_nfs_get_handle() < 0)
return -1;
if (root_nfs_do_mount(sb) < 0)
return -1;
root_nfs_close();
return 0;
}
/.depend
0,0 → 1,134
bio.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfsiod.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
dir.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
file.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
inode.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfsiod.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smp.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smp_lock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/unistd.h
nfsiod.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/rpcsock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfsiod.h
nfsroot.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/config.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/random.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/param.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/utsname.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/if.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/inet.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/net.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/netdevice.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/if_arp.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/net/ax25.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/skbuff.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/socket.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/route.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_mount.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/net/route.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/net/sock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
proc.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/param.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/utsname.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
rpcsock.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/socket.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/net.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/rpcsock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/udp.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/net/sock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
sock.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/socket.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/net.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/rpcsock.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
symlink.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/nfs_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
/Makefile
0,0 → 1,20
#
# Makefile for the linux nfs-filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
 
O_TARGET := nfs.o
O_OBJS := proc.o sock.o rpcsock.o inode.o file.o bio.o \
nfsiod.o dir.o symlink.o
 
ifdef CONFIG_ROOT_NFS
O_OBJS += nfsroot.o
endif
 
M_OBJS := $(O_TARGET)
 
include $(TOPDIR)/Rules.make
/README
0,0 → 1,114
 
 
This is an NFS client for Linux that supports async RPC calls for
read-ahead (and hopefully soon, write-back) on regular files.
 
The implementation uses a straightforward nfsiod scheme. After
trying out a number of different concepts, I finally got back to
this concept, because everything else either didn't work or gave me
headaches. It's not flashy, but it works without hacking into any
other regions of the kernel.
 
 
HOW TO USE
 
This stuff compiles as a loadable module (I developed it on 1.3.77).
Simply type mkmodule, and insmod nfs.o. This will start four nfsiod's
at the same time (which will show up under the pseudonym of insmod in
ps-style listings).
 
Alternatively, you can put it right into the kernel: remove everything
from fs/nfs, move the Makefile and all *.c to this directory, and
copy all *.h files to include/linux.
 
After mounting, you should be able to watch (with tcpdump) several
RPC READ calls being placed simultaneously.
 
 
HOW IT WORKS
 
When a process reads from a file on an NFS volume, the following
happens:
 
* nfs_file_read sets file->f_reada if more than 1K is
read at once. It then calls generic_file_read.
 
* generic_file_read requests one ore more pages via
nfs_readpage.
 
* nfs_readpage allocates a request slot with an nfsiod
daemon, fills in the READ request, sends out the
RPC call, kicks the daemon, and returns.
If there's no free biod, nfs_readpage places the
call directly, waiting for the reply (sync readpage).
 
* nfsiod calls nfs_rpc_doio to collect the reply. If the
call was successful, it sets page->uptodate and
wakes up all processes waiting on page->wait;
 
This is the rough outline only. There are a few things to note:
 
* Async RPC will not be tried when server->rsize < PAGE_SIZE.
 
* When an error occurs, nfsiod has no way of returning
the error code to the user process. Therefore, it flags
page->error and wakes up all processes waiting on that
page (they usually do so from within generic_readpage).
 
generic_readpage finds that the page is still not
uptodate, and calls nfs_readpage again. This time around,
nfs_readpage notices that page->error is set and
unconditionally does a synchronous RPC call.
 
This area needs a lot of improvement, since read errors
are not that uncommon (e.g. we have to retransmit calls
if the fsuid is different from the ruid in order to
cope with root squashing and stuff like this).
 
Retransmits with fsuid/ruid change should be handled by
nfsiod, but this doesn't come easily (a more general nfs_call
routine that does all this may be useful...)
 
* To save some time on readaheads, we save one data copy
by frobbing the page into the iovec passed to the
RPC code so that the networking layer copies the
data into the page directly.
 
This needs to be adjustable (different authentication
flavors; AUTH_NULL versus AUTH_SHORT verifiers).
 
* Currently, a fixed number of nfsiod's is spawned from
within init_nfs_fs. This is problematic when running
as a loadable module, because this will keep insmod's
memory allocated. As a side-effect, you will see the
nfsiod processes listed as several insmod's when doing
a `ps.'
 
* This NFS client implements server congestion control via
Van Jacobson slow start as implemented in 44BSD. I haven't
checked how well this behaves, but since Rick Macklem did
it this way, it should be okay :-)
 
 
WISH LIST
 
After giving this thing some testing, I'd like to add some more
features:
 
* Some sort of async write handling. True write-back doesn't
work with the current kernel (I think), because invalidate_pages
kills all pages, regardless of whether they're dirty or not.
Besides, this may require special bdflush treatment because
write caching on clients is really hairy.
 
Alternatively, a write-through scheme might be useful where
the client enqueues the request, but leaves collecting the
results to nfsiod. Again, we need a way to pass RPC errors
back to the application.
 
* Support for different authentication flavors.
 
* /proc/net/nfsclnt (for nfsstat, etc.).
 
March 29, 1996
Olaf Kirch <okir@monad.swb.de>

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.