summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/Makefile31
-rw-r--r--fs/nfs/dir.c609
-rw-r--r--fs/nfs/file.c237
-rw-r--r--fs/nfs/inode.c240
-rw-r--r--fs/nfs/mmap.c103
-rw-r--r--fs/nfs/proc.c931
-rw-r--r--fs/nfs/sock.c242
-rw-r--r--fs/nfs/symlink.c116
8 files changed, 2509 insertions, 0 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
new file mode 100644
index 000000000..8610c95b1
--- /dev/null
+++ b/fs/nfs/Makefile
@@ -0,0 +1,31 @@
+#
+# 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...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= proc.o sock.o inode.o file.o dir.o \
+ symlink.o mmap.o
+
+nfs.o: $(OBJS)
+ $(LD) -r -o nfs.o $(OBJS)
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
new file mode 100644
index 000000000..62a3e0821
--- /dev/null
+++ b/fs/nfs/dir.c
@@ -0,0 +1,609 @@
+/*
+ * linux/fs/nfs/dir.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * nfs directory handling functions
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/nfs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h> /* for fs functions */
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+static int nfs_dir_read(struct inode *, struct file *filp, char *buf,
+ int count);
+static int nfs_readdir(struct inode *, struct file *, struct dirent *, int);
+static int nfs_lookup(struct inode *dir, const char *name, int len,
+ struct inode **result);
+static int nfs_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result);
+static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode);
+static int nfs_rmdir(struct inode *dir, const char *name, int len);
+static int nfs_unlink(struct inode *dir, const char *name, int len);
+static int nfs_symlink(struct inode *inode, const char *name, int len,
+ const char *symname);
+static int nfs_link(struct inode *oldinode, struct inode *dir,
+ const char *name, int len);
+static int nfs_mknod(struct inode *dir, const char *name, int len, int mode,
+ int rdev);
+static int nfs_rename(struct inode *old_dir, const char *old_name,
+ int old_len, struct inode *new_dir, const char *new_name,
+ int new_len);
+
+static struct file_operations nfs_dir_operations = {
+ NULL, /* lseek - default */
+ nfs_dir_read, /* read - bad */
+ NULL, /* write - bad */
+ nfs_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations nfs_dir_inode_operations = {
+ &nfs_dir_operations, /* default directory file-ops */
+ nfs_create, /* create */
+ nfs_lookup, /* lookup */
+ nfs_link, /* link */
+ nfs_unlink, /* unlink */
+ nfs_symlink, /* symlink */
+ nfs_mkdir, /* mkdir */
+ nfs_rmdir, /* rmdir */
+ nfs_mknod, /* mknod */
+ nfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf,
+ int count)
+{
+ return -EISDIR;
+}
+
+/*
+ * We need to do caching of directory entries to prevent an
+ * incredible amount of RPC traffic. Only the most recent open
+ * directory is cached. This seems sufficient for most purposes.
+ * Technically, we ought to flush the cache on close but this is
+ * not a problem in practice.
+ */
+
+static int nfs_readdir(struct inode *inode, struct file *filp,
+ struct dirent *dirent, int count)
+{
+ static int c_dev = 0;
+ static int c_ino;
+ static int c_size;
+ static struct nfs_entry *c_entry = NULL;
+
+ int result;
+ int i;
+ struct nfs_entry *entry;
+
+ if (!inode || !S_ISDIR(inode->i_mode)) {
+ printk("nfs_readdir: inode is NULL or not a directory\n");
+ return -EBADF;
+ }
+
+ /* initialize cache memory if it hasn't been used before */
+
+ if (c_entry == NULL) {
+ i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE;
+ c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL);
+ for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
+ c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
+ GFP_KERNEL);
+ }
+ }
+ entry = NULL;
+
+ /* try to find it in the cache */
+
+ if (inode->i_dev == c_dev && inode->i_ino == c_ino) {
+ for (i = 0; i < c_size; i++) {
+ if (filp->f_pos == c_entry[i].cookie) {
+ if (i == c_size - 1) {
+ if (c_entry[i].eof)
+ return 0;
+ }
+ else
+ entry = c_entry + i + 1;
+ break;
+ }
+ }
+ }
+
+ /* if we didn't find it in the cache, revert to an nfs call */
+
+ if (!entry) {
+ result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+ filp->f_pos, NFS_READDIR_CACHE_SIZE, c_entry);
+ if (result < 0) {
+ c_dev = 0;
+ return result;
+ }
+ if (result > 0) {
+ c_dev = inode->i_dev;
+ c_ino = inode->i_ino;
+ c_size = result;
+ entry = c_entry + 0;
+ }
+ }
+
+ /* if we found it in the cache or from an nfs call, return results */
+
+ if (entry) {
+ i = strlen(entry->name);
+ memcpy_tofs(dirent->d_name, entry->name, i + 1);
+ put_fs_long(entry->fileid, &dirent->d_ino);
+ put_fs_word(i, &dirent->d_reclen);
+ filp->f_pos = entry->cookie;
+ return ROUND_UP(NAME_OFFSET(dirent)+i+1);
+ }
+ return 0;
+}
+
+/*
+ * Lookup caching is a big win for performance but this is just
+ * a trial to see how well it works on a small scale.
+ * For example, bash does a lookup on ".." 13 times for each path
+ * element when running pwd. Yes, hard to believe but true.
+ * Try pwd in a filesystem mounted with noac.
+ *
+ * It trades a little cpu time and memory for a lot of network bandwidth.
+ * Since the cache is not hashed yet, it is a good idea not to make it too
+ * large because every lookup looks through the entire cache even
+ * though most of them will fail.
+ */
+
+static struct nfs_lookup_cache_entry {
+ int dev;
+ int inode;
+ char filename[NFS_MAXNAMLEN + 1];
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
+ int expiration_date;
+} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
+
+static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
+ const char *filename)
+{
+ struct nfs_lookup_cache_entry *entry;
+ int i;
+
+ for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
+ entry = nfs_lookup_cache + i;
+ if (entry->dev == dir->i_dev && entry->inode == dir->i_ino
+ && !strncmp(filename, entry->filename, NFS_MAXNAMLEN))
+ return entry;
+ }
+ return NULL;
+}
+
+static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename,
+ struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ static int nfs_lookup_cache_in_use = 0;
+
+ struct nfs_lookup_cache_entry *entry;
+
+ if (!nfs_lookup_cache_in_use) {
+ memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache));
+ nfs_lookup_cache_in_use = 1;
+ }
+ if ((entry = nfs_lookup_cache_index(dir, filename))) {
+ if (jiffies > entry->expiration_date) {
+ entry->dev = 0;
+ return 0;
+ }
+ *fhandle = entry->fhandle;
+ *fattr = entry->fattr;
+ return 1;
+ }
+ return 0;
+}
+
+static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
+ struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ static int nfs_lookup_cache_pos = 0;
+ struct nfs_lookup_cache_entry *entry;
+
+ /* compensate for bug in SGI NFS server */
+ if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1
+ || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1)
+ return;
+ if (!(entry = nfs_lookup_cache_index(dir, filename))) {
+ entry = nfs_lookup_cache + nfs_lookup_cache_pos++;
+ if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE)
+ nfs_lookup_cache_pos = 0;
+ }
+ entry->dev = dir->i_dev;
+ entry->inode = dir->i_ino;
+ strcpy(entry->filename, filename);
+ entry->fhandle = *fhandle;
+ entry->fattr = *fattr;
+ entry->expiration_date = jiffies + (S_ISDIR(fattr->mode)
+ ? NFS_SERVER(dir)->acdirmax : NFS_SERVER(dir)->acregmax);
+}
+
+static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
+ const char *filename)
+{
+ struct nfs_lookup_cache_entry *entry;
+ int dev;
+ int fileid;
+ int i;
+
+ if (inode) {
+ dev = inode->i_dev;
+ fileid = inode->i_ino;
+ }
+ else if ((entry = nfs_lookup_cache_index(dir, filename))) {
+ dev = entry->dev;
+ fileid = entry->fattr.fileid;
+ }
+ else
+ return;
+ for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
+ entry = nfs_lookup_cache + i;
+ if (entry->dev == dev && entry->fattr.fileid == fileid)
+ entry->dev = 0;
+ }
+}
+
+static void nfs_lookup_cache_refresh(struct inode *file,
+ struct nfs_fattr *fattr)
+{
+ struct nfs_lookup_cache_entry *entry;
+ int dev = file->i_dev;
+ int fileid = file->i_ino;
+ int i;
+
+ for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
+ entry = nfs_lookup_cache + i;
+ if (entry->dev == dev && entry->fattr.fileid == fileid)
+ entry->fattr = *fattr;
+ }
+}
+
+static int nfs_lookup(struct inode *dir, const char *__name, int len,
+ struct inode **result)
+{
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
+ char name[len > NFS_MAXNAMLEN? 1 : len+1];
+ int error;
+
+ *result = NULL;
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("nfs_lookup: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (len > NFS_MAXNAMLEN) {
+ iput(dir);
+ return -ENAMETOOLONG;
+ }
+ 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;
+ }
+ 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)
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ 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)
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ iput(dir);
+ 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);
+ if (!error)
+ nfs_lookup_cache_remove(dir, NULL, name);
+ iput(dir);
+ return error;
+}
+
+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;
+ }
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
+ if (!error)
+ 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);
+ if (!error)
+ nfs_lookup_cache_remove(dir, oldinode, NULL);
+ 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 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);
+ if (!error) {
+ 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;
+ inode->i_mode = fattr->mode;
+ inode->i_nlink = fattr->nlink;
+ inode->i_uid = fattr->uid;
+ inode->i_gid = fattr->gid;
+ inode->i_size = fattr->size;
+ inode->i_blksize = fattr->blocksize;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = 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 (was_empty) {
+ 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))
+ init_fifo(inode);
+ else
+ inode->i_op = NULL;
+ }
+ nfs_lookup_cache_refresh(inode, fattr);
+}
+
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
new file mode 100644
index 000000000..e71d29483
--- /dev/null
+++ b/fs/nfs/file.c
@@ -0,0 +1,237 @@
+/*
+ * 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).
+ *
+ * nfs regular file handling functions
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#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>
+
+static int nfs_file_read(struct inode *, struct file *, char *, int);
+static int nfs_file_write(struct inode *, struct file *, 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_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 */
+ NULL, /* bmap */
+ NULL /* truncate */
+};
+
+/* Once data is inserted, it can only be deleted, if (in_use==0). */
+struct read_cache {
+ int in_use; /* currently in use? */
+ unsigned long inode_num; /* inode number */
+ off_t file_pos; /* file position */
+ int len; /* size of data */
+ unsigned long time; /* time, this entry was inserted */
+ char * buf; /* data */
+ int buf_size; /* size of buffer */
+};
+
+#define READ_CACHE_SIZE 5
+#define EXPIRE_CACHE (HZ * 3) /* keep no longer than 3 seconds */
+
+unsigned long num_requests = 0;
+unsigned long num_cache_hits = 0;
+
+static int tail = 0; /* next cache slot to replace */
+
+static struct read_cache cache[READ_CACHE_SIZE] = {
+ { 0, 0, -1, 0, 0, NULL, 0 },
+ { 0, 0, -1, 0, 0, NULL, 0 },
+ { 0, 0, -1, 0, 0, NULL, 0 },
+ { 0, 0, -1, 0, 0, NULL, 0 },
+ { 0, 0, -1, 0, 0, NULL, 0 } };
+
+static int nfs_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int nfs_file_read(struct inode *inode, struct file *file, char *buf,
+ int count)
+{
+ int result, hunk, i, n, fs;
+ struct nfs_fattr fattr;
+ char *data;
+ off_t pos;
+
+ if (!inode) {
+ printk("nfs_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("nfs_file_read: read from non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+ pos = file->f_pos;
+ if (pos + count > inode->i_size)
+ count = inode->i_size - pos;
+ if (count <= 0)
+ return 0;
+ ++num_requests;
+ cli();
+ for (i = 0; i < READ_CACHE_SIZE; i++)
+ if ((cache[i].inode_num == inode->i_ino)
+ && (cache[i].file_pos <= pos)
+ && (cache[i].file_pos + cache[i].len >= pos + count)
+ && (abs(jiffies - cache[i].time) <= EXPIRE_CACHE))
+ break;
+ if (i < READ_CACHE_SIZE) {
+ ++cache[i].in_use;
+ sti();
+ ++num_cache_hits;
+ memcpy_tofs(buf, cache[i].buf + pos - cache[i].file_pos, count);
+ --cache[i].in_use;
+ file->f_pos += count;
+ return count;
+ }
+ sti();
+ n = NFS_SERVER(inode)->rsize;
+ for (i = 0; i < count - n; i += n) {
+ result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+ pos, n, buf, &fattr, 1);
+ if (result < 0)
+ return result;
+ pos += result;
+ buf += result;
+ if (result < n) {
+ file->f_pos = pos;
+ nfs_refresh_inode(inode, &fattr);
+ return i + result;
+ }
+ }
+ fs = 0;
+ if (!(data = (char *)kmalloc(n, GFP_KERNEL))) {
+ data = buf;
+ fs = 1;
+ }
+ result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+ pos, n, data, &fattr, fs);
+ if (result < 0) {
+ if (!fs)
+ kfree_s(data, n);
+ return result;
+ }
+ hunk = count - i;
+ if (result < hunk)
+ hunk = result;
+ if (fs) {
+ file->f_pos = pos + hunk;
+ nfs_refresh_inode(inode, &fattr);
+ return i + hunk;
+ }
+ memcpy_tofs(buf, data, hunk);
+ file->f_pos = pos + hunk;
+ nfs_refresh_inode(inode, &fattr);
+ cli();
+ if (cache[tail].in_use == 0) {
+ if (cache[tail].buf)
+ kfree_s(cache[tail].buf, cache[tail].buf_size);
+ cache[tail].buf = data;
+ cache[tail].buf_size = n;
+ cache[tail].inode_num = inode->i_ino;
+ cache[tail].file_pos = pos;
+ cache[tail].len = result;
+ cache[tail].time = jiffies;
+ if (++tail >= READ_CACHE_SIZE)
+ tail = 0;
+ } else
+ kfree_s(data, n);
+ sti();
+ return i + hunk;
+}
+
+static int nfs_file_write(struct inode *inode, struct file *file, char *buf,
+ int count)
+{
+ int result, hunk, i, n, pos;
+ struct nfs_fattr fattr;
+
+ 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;
+
+ cli();
+ /* If hit, cache is dirty and must be expired. */
+ for (i = 0; i < READ_CACHE_SIZE; i++)
+ if(cache[i].inode_num == inode->i_ino)
+ cache[i].time -= EXPIRE_CACHE;
+ sti();
+
+ pos = file->f_pos;
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+ n = NFS_SERVER(inode)->wsize;
+ for (i = 0; i < count; i += n) {
+ hunk = count - i;
+ if (hunk >= n)
+ hunk = n;
+ result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+ pos, hunk, buf, &fattr);
+ if (result < 0)
+ return result;
+ pos += hunk;
+ buf += hunk;
+ if (hunk < n) {
+ i += hunk;
+ break;
+ }
+ }
+ file->f_pos = pos;
+ nfs_refresh_inode(inode, &fattr);
+ return i;
+}
+
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
new file mode 100644
index 000000000..17f2cb6f0
--- /dev/null
+++ b/fs/nfs/inode.c
@@ -0,0 +1,240 @@
+/*
+ * linux/fs/nfs/inode.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * nfs inode and superblock handling functions
+ */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/nfs_fs.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>
+
+extern int close_fp(struct file *filp, unsigned int fd);
+
+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_statfs(struct super_block *, struct statfs *);
+
+static struct super_operations nfs_sops = {
+ NULL, /* 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
+};
+
+static void nfs_put_inode(struct inode * inode)
+{
+ clear_inode(inode);
+}
+
+void nfs_put_super(struct super_block *sb)
+{
+ /* No locks should be open on this, so 0 should be safe as a fd. */
+ close_fp(sb->u.nfs_sb.s_server.file, 0);
+ lock_super(sb);
+ sb->s_dev = 0;
+ unlock_super(sb);
+}
+
+/*
+ * 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 theses 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;
+ dev_t dev = sb->s_dev;
+
+ if (!data) {
+ printk("nfs_read_super: missing data argument\n");
+ sb->s_dev = 0;
+ 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;
+ return NULL;
+ }
+ if (!S_ISSOCK(filp->f_inode->i_mode)) {
+ printk("nfs_read_super: not a socket\n");
+ sb->s_dev = 0;
+ 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);
+ 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");
+ return NULL;
+ }
+ return sb;
+}
+
+void nfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ int error;
+ struct nfs_fsinfo res;
+
+ put_fs_long(NFS_SUPER_MAGIC, &buf->f_type);
+ 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;
+ }
+ put_fs_long(res.bsize, &buf->f_bsize);
+ put_fs_long(res.blocks, &buf->f_blocks);
+ put_fs_long(res.bfree, &buf->f_bfree);
+ put_fs_long(res.bavail, &buf->f_bavail);
+ put_fs_long(0, &buf->f_files);
+ put_fs_long(0, &buf->f_ffree);
+ /* We should really try to interrogate the remote server to find
+ it's maximum name length here */
+ put_fs_long(NAME_MAX, &buf->f_namelen);
+}
+
+/*
+ * 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;
+
+ if (attr->ia_valid & ATTR_MODE)
+ sattr.mode = attr->ia_mode;
+ else
+ sattr.mode = (unsigned) -1;
+
+ if (attr->ia_valid & ATTR_UID)
+ sattr.uid = attr->ia_uid;
+ else
+ sattr.uid = (unsigned) -1;
+
+ if (attr->ia_valid & ATTR_GID)
+ sattr.gid = attr->ia_gid;
+ else
+ sattr.gid = (unsigned) -1;
+
+ if (attr->ia_valid & ATTR_SIZE)
+ sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1;
+ else
+ sattr.size = (unsigned) -1;
+
+ if (attr->ia_valid & ATTR_MTIME) {
+ sattr.mtime.seconds = attr->ia_mtime;
+ sattr.mtime.useconds = 0;
+ } else
+ sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
+
+ if (attr->ia_valid & ATTR_ATIME) {
+ sattr.atime.seconds = attr->ia_atime;
+ sattr.atime.useconds = 0;
+ } else
+ sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
+
+ 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;
+}
diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c
new file mode 100644
index 000000000..811176a69
--- /dev/null
+++ b/fs/nfs/mmap.c
@@ -0,0 +1,103 @@
+/*
+ * fs/nfs/mmap.c by Jon Tombs 15 Aug 1993
+ *
+ * This code is from
+ * linux/mm/mmap.c which was written by obz, Linus and Eric
+ * and
+ * linux/mm/memory.c by Linus Torvalds and others
+ *
+ * Copyright (C) 1993
+ *
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/nfs_fs.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area,
+ unsigned long address, unsigned long page, int no_share)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned int clear;
+ unsigned long tmp;
+ int n;
+ int i;
+ int pos;
+ struct nfs_fattr fattr;
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ if (address + PAGE_SIZE > area->vm_end) {
+ clear = address + PAGE_SIZE - area->vm_end;
+ }
+
+ n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
+
+ for (i = 0; i < (PAGE_SIZE - clear); i += n) {
+ int hunk, result;
+
+ hunk = PAGE_SIZE - i;
+ if (hunk > n)
+ hunk = n;
+ result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+ pos, hunk, (char *) (page + i), &fattr, 0);
+ if (result < 0)
+ break;
+ pos += result;
+ if (result < n) {
+ i += result;
+ break;
+ }
+ }
+
+#ifdef doweneedthishere
+ nfs_refresh_inode(inode, &fattr);
+#endif
+
+ tmp = page + PAGE_SIZE;
+ while (clear--) {
+ *(char *)--tmp = 0;
+ }
+ return page;
+}
+struct vm_operations_struct nfs_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ nfs_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* share */
+ NULL, /* unmap */
+};
+
+
+/* This is used for a general mmap of a nfs file */
+int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+ if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &nfs_file_mmap;
+ return 0;
+}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
new file mode 100644
index 000000000..fd01dad99
--- /dev/null
+++ b/fs/nfs/proc.c
@@ -0,0 +1,931 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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/config.h>
+#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 <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, 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,
+ int fs)
+{
+ unsigned len = *lenp = ntohl(*p++);
+ if (len > maxlen)
+ return NULL;
+ if (fs)
+ memcpy_tofs(data, p, len);
+ else
+ 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++);
+ 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 fs)
+{
+ 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, fs))) {
+ 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_write(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;
+
+ 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 */
+ 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) {
+ 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 *p, *p0;
+ int status;
+ int ruid = 0;
+
+ 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.
+ */
+
+static int *nfs_rpc_header(int *p, int procedure, int ruid)
+{
+ 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(NFS_PROGRAM);
+ *p++ = htonl(NFS_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(ruid ? current->uid : current->fsuid);
+ *p++ = htonl(current->egid);
+ p2 = p++;
+ for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++)
+ *p++ = htonl(current->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_verify(int *p)
+{
+ unsigned int n;
+
+ p++;
+ if ((n = ntohl(*p++)) != RPC_REPLY) {
+ printk("nfs_rpc_verify: not an RPC reply: %d\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;
+}
+
+/*
+ * 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_ACCES, EACCES },
+ { NFSERR_EXIST, EEXIST },
+ { 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;
+}
+
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
new file mode 100644
index 000000000..2455d938a
--- /dev/null
+++ b/fs/nfs/sock.c
@@ -0,0 +1,242 @@
+/*
+ * 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/config.h>
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <asm/segment.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+
+/* JEJB/JSP 2/7/94
+ * this must match the value of NFS_SLACK_SPACE in linux/fs/nfs/proc.c
+ * ***FIXME*** should probably put this in nfs_fs.h */
+#define NFS_SLACK_SPACE 1024
+
+
+extern struct socket *socki_lookup(struct inode *inode);
+
+#define _S(nr) (1<<((nr)-1))
+
+/*
+ * We violate some modularity principles here by poking around
+ * in some socket internals. Besides having to call socket
+ * functions from kernel-space instead of user space, the socket
+ * interface does not lend itself well to being cleanly called
+ * without a file descriptor. Since the nfs calls can run on
+ * behalf of any process, the superblock maintains a file pointer
+ * to the server socket.
+ */
+
+static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ unsigned short fs;
+ int result;
+ int xid;
+ int len;
+ select_table wait_table;
+ struct select_table_entry entry;
+ int (*select) (struct inode *, struct file *, int, select_table *);
+ int init_timeout, max_timeout;
+ int timeout;
+ int retrans;
+ int major_timeout_seen;
+ char *server_name;
+ int n;
+ int addrlen;
+ unsigned long old_mask;
+ /* JEJB/JSP 2/7/94
+ * This is for a 4 byte recv of the xid only */
+ int recv_xid;
+
+ xid = start[0];
+ len = ((char *) end) - ((char *) start);
+ file = server->file;
+ inode = file->f_inode;
+ select = file->f_op->select;
+ sock = socki_lookup(inode);
+ if (!sock) {
+ printk("nfs_rpc_call: socki_lookup failed\n");
+ return -EBADF;
+ }
+ init_timeout = server->timeo;
+ max_timeout = NFS_MAX_RPC_TIMEOUT*HZ/10;
+ retrans = server->retrans;
+ major_timeout_seen = 0;
+ server_name = server->hostname;
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL)
+#if 0
+ | _S(SIGSTOP)
+#endif
+ | ((server->flags & NFS_MOUNT_INTR)
+ ? ((current->sigaction[SIGINT - 1].sa_handler == SIG_DFL
+ ? _S(SIGINT) : 0)
+ | (current->sigaction[SIGQUIT - 1].sa_handler == SIG_DFL
+ ? _S(SIGQUIT) : 0))
+ : 0));
+ fs = get_fs();
+ set_fs(get_ds());
+ for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) {
+ result = sock->ops->send(sock, (void *) start, len, 0, 0);
+ if (result < 0) {
+ printk("nfs_rpc_call: send error = %d\n", result);
+ break;
+ }
+ re_select:
+ wait_table.nr = 0;
+ wait_table.entry = &entry;
+ current->state = TASK_INTERRUPTIBLE;
+ if (!select(inode, file, SEL_IN, &wait_table)
+ && !select(inode, file, SEL_IN, NULL)) {
+ if (timeout > max_timeout) {
+ /* JEJB/JSP 2/7/94
+ * This is useful to see if the system is
+ * hanging */
+ printk("NFS max timeout reached on %s\n",
+ server_name);
+ timeout = max_timeout;
+ }
+ current->timeout = jiffies + timeout;
+ schedule();
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ if (current->signal & ~current->blocked) {
+ current->timeout = 0;
+ result = -ERESTARTSYS;
+ break;
+ }
+ if (!current->timeout) {
+ if (n < retrans)
+ continue;
+ if (server->flags & NFS_MOUNT_SOFT) {
+ printk("NFS server %s not responding, "
+ "timed out\n", server_name);
+ result = -EIO;
+ break;
+ }
+ n = 0;
+ timeout = init_timeout;
+ init_timeout <<= 1;
+ if (!major_timeout_seen) {
+ printk("NFS server %s not responding, "
+ "still trying\n", server_name);
+ }
+ major_timeout_seen = 1;
+ continue;
+ }
+ else
+ current->timeout = 0;
+ }
+ else if (wait_table.nr)
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ addrlen = 0;
+ /* JEJB/JSP 2/7/94
+ * Get the xid from the next packet using a peek, so keep it
+ * on the recv queue. If it is wrong, it will be some reply
+ * we don't now need, so discard it */
+ result = sock->ops->recvfrom(sock, (void *)&recv_xid,
+ sizeof(recv_xid), 1, MSG_PEEK,
+ NULL, &addrlen);
+ if (result < 0) {
+ if (result == -EAGAIN) {
+#if 0
+ printk("nfs_rpc_call: bad select ready\n");
+#endif
+ goto re_select;
+ }
+ if (result == -ECONNREFUSED) {
+#if 0
+ printk("nfs_rpc_call: server playing coy\n");
+#endif
+ goto re_select;
+ }
+ if (result != -ERESTARTSYS) {
+ printk("nfs_rpc_call: recv error = %d\n",
+ -result);
+ }
+ break;
+ }
+ if (recv_xid == xid) {
+ if (major_timeout_seen)
+ printk("NFS server %s OK\n", server_name);
+ break;
+ }
+ /* JEJB/JSP 2/7/94
+ * we have xid mismatch, so discard the packet and start
+ * again. What a hack! but I can't call recvfrom with
+ * a null buffer yet. */
+ (void)sock->ops->recvfrom(sock, (void *)&recv_xid,
+ sizeof(recv_xid), 1, 0, NULL,
+ &addrlen);
+#if 0
+ printk("nfs_rpc_call: XID mismatch\n");
+#endif
+ goto re_select;
+ }
+ /* JEJB/JSP 2/7/94
+ *
+ * we have the correct xid, so read into the correct place and
+ * return it
+ *
+ */
+ result=sock->ops->recvfrom(sock, (void *)start,
+ size + 1024, 1, 0, NULL,
+ /* Here is NFS_SLACK_SPACE..., hack */
+ &addrlen);
+ if (result < 0) {
+ printk("NFS: notice message: result=%d\n", result);
+ } else if (result < addrlen) {
+ printk("NFS: just caught a too small read memory size..., email to NET channel\n");
+ printk("NFS: result=%d,addrlen=%d\n", result, addrlen);
+ result = -EIO;
+ }
+ current->blocked = old_mask;
+ set_fs(fs);
+ return result;
+}
+
+/*
+ * For now we lock out other simultaneous nfs calls for the same filesystem
+ * because we are single-threaded and don't want to get mismatched
+ * RPC replies.
+ */
+
+int nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
+{
+ int result;
+
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+ result = do_nfs_rpc_call(server, start, end, size);
+ server->lock = 0;
+ wake_up(&server->wait);
+ return result;
+}
+
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
new file mode 100644
index 000000000..4cbe631c6
--- /dev/null
+++ b/fs/nfs/symlink.c
@@ -0,0 +1,116 @@
+/*
+ * linux/fs/nfs/symlink.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * Optimization changes Copyright (C) 1994 Florian La Roche
+ *
+ * nfs symlink handling code
+ */
+
+#include <asm/segment.h>
+
+#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>
+
+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, /* 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_fs_byte('\0', buffer + len);
+ error = len;
+ }
+ kfree(mem);
+ return error;
+}