summaryrefslogtreecommitdiffstats
path: root/fs/ncpfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /fs/ncpfs
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/ncpfs')
-rw-r--r--fs/ncpfs/Makefile21
-rw-r--r--fs/ncpfs/dir.c1283
-rw-r--r--fs/ncpfs/file.c281
-rw-r--r--fs/ncpfs/inode.c570
-rw-r--r--fs/ncpfs/ioctl.c160
-rw-r--r--fs/ncpfs/mmap.c155
-rw-r--r--fs/ncpfs/ncplib_kernel.c627
-rw-r--r--fs/ncpfs/ncplib_kernel.h169
-rw-r--r--fs/ncpfs/sock.c704
9 files changed, 3970 insertions, 0 deletions
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
new file mode 100644
index 000000000..be43c491b
--- /dev/null
+++ b/fs/ncpfs/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux ncp-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 := ncpfs.o
+O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o
+M_OBJS := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG_NCP=1
+
+include $(TOPDIR)/Rules.make
+
+ncplib_kernel.o: ncplib_kernel.c ncplib_kernel.h
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -finline-functions -c -o $@ $<
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
new file mode 100644
index 000000000..ede71765f
--- /dev/null
+++ b/fs/ncpfs/dir.c
@@ -0,0 +1,1283 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/ncp_fs.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include "ncplib_kernel.h"
+
+struct ncp_dirent {
+ struct nw_info_struct i;
+ struct nw_search_sequence s; /* given back for i */
+ unsigned long f_pos;
+};
+
+static long
+ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
+
+static int
+ncp_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir);
+
+static int
+ncp_read_volume_list(struct ncp_server *server, int start_with,
+ int cache_size);
+
+static int
+ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
+ int cache_size, struct ncp_dirent *entry);
+
+static struct inode *
+ncp_iget(struct inode *dir, struct nw_file_info *finfo);
+
+static struct ncp_inode_info *
+ncp_find_dir_inode(struct inode *dir, const char *name);
+
+static int
+ncp_lookup(struct inode *dir, const char *__name,
+ int len, struct inode **result);
+
+static int
+ncp_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result);
+
+static int
+ncp_mkdir(struct inode *dir, const char *name, int len, int mode);
+
+static int
+ncp_rmdir(struct inode *dir, const char *name, int len);
+
+static int
+ncp_unlink(struct inode *dir, const char *name, int len);
+
+static int
+ncp_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);
+
+static inline void
+str_upper(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ {
+ *name -= ('a' - 'A');
+ }
+ name++;
+ }
+}
+
+static inline void
+str_lower(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'A' && *name <= 'Z')
+ {
+ *name += ('a' - 'A');
+ }
+ name ++;
+ }
+}
+
+static inline int
+ncp_namespace(struct inode *i)
+{
+ struct ncp_server *server = NCP_SERVER(i);
+ struct nw_info_struct *info = NCP_ISTRUCT(i);
+ return server->name_space[info->volNumber];
+}
+
+static inline int
+ncp_preserve_case(struct inode *i)
+{
+ return (ncp_namespace(i) == NW_NS_OS2);
+}
+
+static struct file_operations ncp_dir_operations = {
+ NULL, /* lseek - default */
+ ncp_dir_read, /* read - bad */
+ NULL, /* write - bad */
+ ncp_readdir, /* readdir */
+ NULL, /* select - default */
+ ncp_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations ncp_dir_inode_operations = {
+ &ncp_dir_operations, /* default directory file ops */
+ ncp_create, /* create */
+ ncp_lookup, /* lookup */
+ NULL, /* link */
+ ncp_unlink, /* unlink */
+ NULL, /* symlink */
+ ncp_mkdir, /* mkdir */
+ ncp_rmdir, /* rmdir */
+ NULL, /* mknod */
+ ncp_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+
+/* Here we encapsulate the inode number handling that depends upon the
+ * mount mode: When we mount a complete server, the memory address of
+ * the ncp_inode_info is used as the inode number. When only a single
+ * volume is mounted, then the dirEntNum is used as the inode
+ * number. As this is unique for the complete volume, this should
+ * enable the NFS exportability of a ncpfs-mounted volume.
+ */
+
+static inline int
+ncp_single_volume(struct ncp_server *server)
+{
+ return (server->m.mounted_vol[0] != '\0');
+}
+
+inline ino_t
+ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info)
+{
+ return ncp_single_volume(server)
+ ? info->finfo.i.dirEntNum : (ino_t)info;
+}
+
+static inline int
+ncp_is_server_root(struct inode *inode)
+{
+ struct ncp_server *s = NCP_SERVER(inode);
+
+ return ( (!ncp_single_volume(s))
+ && (inode->i_ino == ncp_info_ino(s, &(s->root))));
+}
+
+struct ncp_inode_info *
+ncp_find_inode(struct inode *inode)
+{
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct ncp_inode_info *root = &(server->root);
+ struct ncp_inode_info *this = root;
+
+ ino_t ino = inode->i_ino;
+
+ do
+ {
+ if (ino == ncp_info_ino(server, this))
+ {
+ return this;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ return NULL;
+}
+
+static long
+ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+static kdev_t c_dev = 0;
+static unsigned long c_ino = 0;
+static int c_size;
+static int c_seen_eof;
+static int c_last_returned_index;
+static struct ncp_dirent* c_entry = NULL;
+static int c_lock = 0;
+static struct wait_queue *c_wait = NULL;
+
+static inline void
+ncp_lock_dircache(void)
+{
+ while (c_lock)
+ sleep_on(&c_wait);
+ c_lock = 1;
+}
+
+static inline void
+ncp_unlock_dircache(void)
+{
+ c_lock = 0;
+ wake_up(&c_wait);
+}
+
+static int
+ncp_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ int result = 0;
+ int i = 0;
+ int index = 0;
+ struct ncp_dirent *entry = NULL;
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct ncp_inode_info *dir = NCP_INOP(inode);
+
+ DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos);
+ DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
+ inode->i_ino, c_ino);
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ {
+ printk("ncp_readdir: inode is NULL or not a directory\n");
+ return -EBADF;
+ }
+
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+
+ ncp_lock_dircache();
+
+ if (c_entry == NULL)
+ {
+ i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE;
+ c_entry = (struct ncp_dirent *) vmalloc(i);
+ if (c_entry == NULL)
+ {
+ printk("ncp_readdir: no MEMORY for cache\n");
+ result = -ENOMEM;
+ goto finished;
+ }
+ }
+
+ if (filp->f_pos == 0)
+ {
+ ncp_invalid_dir_cache(inode);
+ if (filldir(dirent,".",1, filp->f_pos,
+ ncp_info_ino(server, dir)) < 0)
+ {
+ goto finished;
+ }
+ filp->f_pos += 1;
+ }
+
+ if (filp->f_pos == 1)
+ {
+ if (filldir(dirent,"..",2, filp->f_pos,
+ ncp_info_ino(server, dir->dir)) < 0)
+ {
+ goto finished;
+ }
+ filp->f_pos += 1;
+ }
+
+ 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].f_pos)
+ {
+ entry = &c_entry[i];
+ c_last_returned_index = i;
+ index = i;
+ break;
+ }
+ }
+ if ((entry == NULL) && c_seen_eof)
+ {
+ goto finished;
+ }
+ }
+
+ if (entry == NULL)
+ {
+ int entries;
+ DDPRINTK("ncp_readdir: Not found in cache.\n");
+
+ if (ncp_is_server_root(inode))
+ {
+ entries = ncp_read_volume_list(server, filp->f_pos,
+ NCP_READDIR_CACHE_SIZE);
+ DPRINTK("ncp_read_volume_list returned %d\n", entries);
+
+ }
+ else
+ {
+ entries = ncp_do_readdir(server, inode, filp->f_pos,
+ NCP_READDIR_CACHE_SIZE,
+ c_entry);
+ DPRINTK("ncp_readdir returned %d\n", entries);
+ }
+
+ if (entries < 0)
+ {
+ c_dev = 0;
+ c_ino = 0;
+ result = entries;
+ goto finished;
+ }
+
+ if (entries > 0)
+ {
+ c_seen_eof = (entries < NCP_READDIR_CACHE_SIZE);
+ c_dev = inode->i_dev;
+ c_ino = inode->i_ino;
+ c_size = entries;
+ entry = c_entry;
+ c_last_returned_index = 0;
+ index = 0;
+
+ if (!ncp_preserve_case(inode))
+ {
+ for (i = 0; i < c_size; i++)
+ {
+ str_lower(c_entry[i].i.entryName);
+ }
+ }
+ }
+ }
+
+ if (entry == NULL)
+ {
+ /* Nothing found, even from a ncp call */
+ goto finished;
+ }
+
+ while (index < c_size)
+ {
+ ino_t ino;
+
+ if (ncp_single_volume(server))
+ {
+ ino = (ino_t)(entry->i.dirEntNum);
+ }
+ else
+ {
+ /* For getwd() we have to return the correct
+ * inode in d_ino if the inode is currently in
+ * use. Otherwise the inode number does not
+ * matter. (You can argue a lot about this..) */
+ struct ncp_inode_info *ino_info;
+ ino_info = ncp_find_dir_inode(inode,
+ entry->i.entryName);
+
+ /* Some programs seem to be confused about a
+ * zero inode number, so we set it to one.
+ * Thanks to Gordon Chaffee for this one. */
+ if (ino_info == NULL)
+ {
+ ino_info = (struct ncp_inode_info *) 1;
+ }
+ ino = (ino_t)(ino_info);
+ }
+
+ DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName);
+ DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
+
+ if (filldir(dirent, entry->i.entryName, entry->i.nameLen,
+ entry->f_pos, ino) < 0)
+ {
+ break;
+ }
+
+ if ( (inode->i_dev != c_dev)
+ || (inode->i_ino != c_ino)
+ || (entry->f_pos != filp->f_pos))
+ {
+ /* Someone has destroyed the cache while we slept
+ in filldir */
+ break;
+ }
+ filp->f_pos += 1;
+ index += 1;
+ entry += 1;
+ }
+ finished:
+ ncp_unlock_dircache();
+ return result;
+}
+
+static int
+ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size)
+{
+ struct ncp_dirent *entry = c_entry;
+
+ int total_count = 2;
+ int i;
+
+#if 1
+ if (fpos < 2)
+ {
+ printk("OOPS, we expect fpos >= 2");
+ fpos = 2;
+ }
+#endif
+
+ for (i=0; i<NCP_NUMBER_OF_VOLUMES; i++)
+ {
+ struct ncp_volume_info info;
+
+ if (ncp_get_volume_info_with_number(server, i, &info) != 0)
+ {
+ return (total_count - fpos);
+ }
+
+ if (strlen(info.volume_name) > 0)
+ {
+ if (total_count < fpos)
+ {
+ DPRINTK("ncp_read_volumes: skipped vol: %s\n",
+ info.volume_name);
+ }
+ else if (total_count >= fpos + cache_size)
+ {
+ return (total_count - fpos);
+ }
+ else
+ {
+ DPRINTK("ncp_read_volumes: found vol: %s\n",
+ info.volume_name);
+
+ if (ncp_lookup_volume(server,
+ info.volume_name,
+ &(entry->i)) != 0)
+ {
+ DPRINTK("ncpfs: could not lookup vol "
+ "%s\n", info.volume_name);
+ continue;
+ }
+
+ entry->f_pos = total_count;
+ entry += 1;
+ }
+ total_count += 1;
+ }
+ }
+ return (total_count - fpos);
+}
+
+static int
+ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
+ int cache_size, struct ncp_dirent *entry)
+{
+ static struct nw_search_sequence seq;
+ static struct inode *last_dir;
+ static int total_count;
+
+#if 1
+ if (fpos < 2)
+ {
+ printk("OOPS, we expect fpos >= 2");
+ fpos = 2;
+ }
+#endif
+ DPRINTK("ncp_do_readdir: fpos = %d\n", fpos);
+
+ if (fpos == 2)
+ {
+ last_dir = NULL;
+ total_count = 2;
+ }
+
+ if ((fpos != total_count) || (dir != last_dir))
+ {
+ total_count = 2;
+ last_dir = dir;
+
+ DPRINTK("ncp_do_readdir: re-used seq for %s\n",
+ NCP_ISTRUCT(dir)->entryName);
+
+ if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq)!=0)
+ {
+ DPRINTK("ncp_init_search failed\n");
+ return total_count - fpos;
+ }
+ }
+
+ while (total_count < fpos + cache_size)
+ {
+ if (ncp_search_for_file_or_subdir(server, &seq,
+ &(entry->i)) != 0)
+ {
+ return total_count - fpos;
+ }
+
+ if (total_count < fpos)
+ {
+ DPRINTK("ncp_do_readdir: skipped file: %s\n",
+ entry->i.entryName);
+ }
+ else
+ {
+ DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d",
+ entry->i.entryName, fpos, total_count);
+ entry->s = seq;
+ entry->f_pos = total_count;
+ entry += 1;
+ }
+ total_count += 1;
+ }
+ return (total_count - fpos);
+}
+
+void
+ncp_init_dir_cache(void)
+{
+ c_dev = 0;
+ c_ino = 0;
+ c_entry = NULL;
+}
+
+void
+ncp_invalid_dir_cache(struct inode *ino)
+{
+ if ((ino->i_dev == c_dev) && (ino->i_ino == c_ino))
+ {
+ c_dev = 0;
+ c_ino = 0;
+ c_seen_eof = 0;
+ }
+}
+
+void
+ncp_free_dir_cache(void)
+{
+ DPRINTK("ncp_free_dir_cache: enter\n");
+
+ if (c_entry == NULL)
+ {
+ return;
+ }
+
+ vfree(c_entry);
+ c_entry = NULL;
+
+ DPRINTK("ncp_free_dir_cache: exit\n");
+}
+
+
+static struct inode *
+ncp_iget(struct inode *dir, struct nw_file_info *finfo)
+{
+ struct inode *inode;
+ struct ncp_inode_info *new_inode_info;
+ struct ncp_inode_info *root;
+
+ if (dir == NULL)
+ {
+ printk("ncp_iget: dir is NULL\n");
+ return NULL;
+ }
+
+ if (finfo == NULL)
+ {
+ printk("ncp_iget: finfo is NULL\n");
+ return NULL;
+ }
+
+ new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info),
+ GFP_KERNEL);
+
+ if (new_inode_info == NULL)
+ {
+ printk("ncp_iget: could not alloc mem for %s\n",
+ finfo->i.entryName);
+ return NULL;
+ }
+
+ new_inode_info->state = NCP_INODE_LOOKED_UP;
+ new_inode_info->nused = 0;
+ new_inode_info->dir = NCP_INOP(dir);
+ new_inode_info->finfo = *finfo;
+
+ NCP_INOP(dir)->nused += 1;
+
+ /* We have to link the new inode_info into the doubly linked
+ list of inode_infos to make a complete linear search
+ possible. */
+
+ root = &(NCP_SERVER(dir)->root);
+
+ new_inode_info->prev = root;
+ new_inode_info->next = root->next;
+ root->next->prev = new_inode_info;
+ root->next = new_inode_info;
+
+ if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir),
+ new_inode_info))))
+ {
+ printk("ncp_iget: iget failed!");
+ return NULL;
+ }
+
+ return inode;
+}
+
+void
+ncp_free_inode_info(struct ncp_inode_info *i)
+{
+ if (i == NULL)
+ {
+ printk("ncp_free_inode: i == NULL\n");
+ return;
+ }
+
+ i->state = NCP_INODE_CACHED;
+ while ((i->nused == 0) && (i->state == NCP_INODE_CACHED))
+ {
+ struct ncp_inode_info *dir = i->dir;
+
+ i->next->prev = i->prev;
+ i->prev->next = i->next;
+
+ DDPRINTK("ncp_free_inode_info: freeing %s\n",
+ i->finfo.i.entryName);
+
+ ncp_kfree_s(i, sizeof(struct ncp_inode_info));
+
+ if (dir == i) return;
+
+ (dir->nused)--;
+ i = dir;
+ }
+}
+
+void
+ncp_init_root(struct ncp_server *server)
+{
+ struct ncp_inode_info *root = &(server->root);
+ struct nw_info_struct *i = &(root->finfo.i);
+ unsigned short dummy;
+
+ DPRINTK("ncp_init_root: server %s\n", server->m.server_name);
+ DPRINTK("ncp_init_root: i = %x\n", (int)i);
+
+ root->finfo.opened = 0;
+ i->attributes = aDIR;
+ i->dataStreamSize = 1024;
+ i->dirEntNum = i->DosDirNum = 0;
+ i->volNumber = NCP_NUMBER_OF_VOLUMES+1; /* illegal volnum */
+ ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate));
+ ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate));
+ ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate));
+ i->nameLen = 0;
+ i->entryName[0] = '\0';
+
+ root->state = NCP_INODE_LOOKED_UP;
+ root->nused = 1;
+ root->dir = root;
+ root->next = root->prev = root;
+ return;
+}
+
+int
+ncp_conn_logged_in(struct ncp_server *server)
+{
+ if (server->m.mounted_vol[0] == '\0')
+ {
+ return 0;
+ }
+
+ str_upper(server->m.mounted_vol);
+ if (ncp_lookup_volume(server, server->m.mounted_vol,
+ &(server->root.finfo.i)) != 0)
+ {
+ return -ENOENT;
+ }
+ str_lower(server->root.finfo.i.entryName);
+
+ return 0;
+}
+
+void
+ncp_free_all_inodes(struct ncp_server *server)
+{
+ /* Here nothing should be to do. I do not know whether it's
+ better to leave some memory allocated or be stuck in an
+ endless loop */
+#if 1
+ struct ncp_inode_info *root = &(server->root);
+
+ if (root->next != root)
+ {
+ printk("ncp_free_all_inodes: INODES LEFT!!!\n");
+ }
+
+ while (root->next != root)
+ {
+ printk("ncp_free_all_inodes: freeing inode\n");
+ ncp_free_inode_info(root->next);
+ /* In case we have an endless loop.. */
+ schedule();
+ }
+#endif
+
+ return;
+}
+
+/* We will search the inode that belongs to this name, currently by a
+ complete linear search through the inodes belonging to this
+ filesystem. This has to be fixed. */
+static struct ncp_inode_info *
+ncp_find_dir_inode(struct inode *dir, const char *name)
+{
+ struct ncp_server *server = NCP_SERVER(dir);
+ struct nw_info_struct *dir_info = NCP_ISTRUCT(dir);
+ struct ncp_inode_info *result = &(server->root);
+
+ if (name == NULL)
+ {
+ return NULL;
+ }
+
+ do
+ {
+ if ( (result->dir->finfo.i.dirEntNum == dir_info->dirEntNum)
+ && (result->dir->finfo.i.volNumber == dir_info->volNumber)
+ && (strcmp(result->finfo.i.entryName, name) == 0)
+ /* The root dir is never looked up using this
+ * routine. Without the following test a root
+ * directory 'sys' in a volume named 'sys' could
+ * never be looked up, because
+ * server->root->dir==server->root. */
+ && (result != &(server->root)))
+ {
+ return result;
+ }
+ result = result->next;
+
+ }
+ while (result != &(server->root));
+
+ return NULL;
+}
+
+static int
+ncp_lookup(struct inode *dir, const char *__name, int len,
+ struct inode **result)
+{
+ struct nw_file_info finfo;
+ struct ncp_server *server;
+ struct ncp_inode_info *result_info;
+ int found_in_cache;
+ int down_case = 0;
+ char name[len+1];
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_lookup: inode is NULL or not a directory.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+
+ server = NCP_SERVER(dir);
+
+ if (!ncp_conn_valid(server))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ DPRINTK("ncp_lookup: %s, len %d\n", __name, len);
+
+ /* Fast cheat for . */
+ if (len == 0 || (len == 1 && __name[0] == '.'))
+ {
+ *result = dir;
+ return 0;
+ }
+
+ /* ..and for .. */
+ if (len == 2 && __name[0] == '.' && __name[1] == '.')
+ {
+ struct ncp_inode_info *parent = NCP_INOP(dir)->dir;
+
+ if (parent->state == NCP_INODE_CACHED)
+ {
+ parent->state = NCP_INODE_LOOKED_UP;
+ }
+
+ *result = iget(dir->i_sb, ncp_info_ino(server, parent));
+ iput(dir);
+ if (*result == 0)
+ {
+ return -EACCES;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ memcpy(name, __name, len);
+ name[len] = 0;
+ lock_super(dir->i_sb);
+ result_info = ncp_find_dir_inode(dir, name);
+
+ if (result_info != 0)
+ {
+ if (result_info->state == NCP_INODE_CACHED)
+ {
+ result_info->state = NCP_INODE_LOOKED_UP;
+ }
+
+ /* Here we convert the inode_info address into an
+ inode number */
+
+ *result = iget(dir->i_sb, ncp_info_ino(server, result_info));
+ unlock_super(dir->i_sb);
+ iput(dir);
+
+ if (*result == NULL)
+ {
+ return -EACCES;
+ }
+
+ return 0;
+ }
+
+ /* If the file is in the dir cache, we do not have to ask the
+ server. */
+
+ found_in_cache = 0;
+ ncp_lock_dircache();
+
+ if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino))
+ {
+ int first = c_last_returned_index;
+ int i;
+
+ i = first;
+ do
+ {
+ DDPRINTK("ncp_lookup: trying index: %d, name: %s\n",
+ i, c_entry[i].i.entryName);
+
+ if (strcmp(c_entry[i].i.entryName, name) == 0)
+ {
+ DPRINTK("ncp_lookup: found in cache!\n");
+ finfo.i = c_entry[i].i;
+ found_in_cache = 1;
+ break;
+ }
+ i = (i + 1) % c_size;
+ }
+ while (i != first);
+ }
+ ncp_unlock_dircache();
+
+ if (found_in_cache == 0)
+ {
+ int res;
+
+ DDPRINTK("ncp_lookup: do_lookup on %s/%s\n",
+ NCP_ISTRUCT(dir)->entryName, name);
+
+ if (ncp_is_server_root(dir))
+ {
+ str_upper(name);
+ down_case = 1;
+ res = ncp_lookup_volume(server, name, &(finfo.i));
+ }
+ else
+ {
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(name);
+ down_case = 1;
+ }
+ res = ncp_obtain_info(server,
+ NCP_ISTRUCT(dir)->volNumber,
+ NCP_ISTRUCT(dir)->dirEntNum,
+ name, &(finfo.i));
+ }
+ if (res != 0)
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -ENOENT;
+ }
+ }
+
+ finfo.opened = 0;
+
+ if (down_case != 0)
+ {
+ str_lower(finfo.i.entryName);
+ }
+
+ if (!(*result = ncp_iget(dir, &finfo)))
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EACCES;
+ }
+
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return 0;
+}
+
+static int
+ncp_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result)
+{
+ struct nw_file_info finfo;
+ __u8 _name[len+1];
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_create: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ lock_super(dir->i_sb);
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir), _name,
+ OC_MODE_CREATE|OC_MODE_OPEN|
+ OC_MODE_REPLACE,
+ 0, AR_READ|AR_WRITE,
+ &finfo) != 0)
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EACCES;
+ }
+
+ ncp_invalid_dir_cache(dir);
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_lower(finfo.i.entryName);
+ }
+
+ finfo.access = O_RDWR;
+
+ if (!(*result = ncp_iget(dir, &finfo)) < 0)
+ {
+ ncp_close_file(NCP_SERVER(dir), finfo.file_handle);
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EINVAL;
+ }
+
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return 0;
+}
+
+static int
+ncp_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ int error;
+ struct nw_file_info new_dir;
+ __u8 _name[len+1];
+
+ if ( (name[0] == '.')
+ && ( (len == 1)
+ || ( (len == 2)
+ && (name[1] == '.'))))
+ {
+ iput(dir);
+ return -EEXIST;
+ }
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_mkdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir), _name,
+ OC_MODE_CREATE, aDIR, 0xffff,
+ &new_dir) != 0)
+ {
+ error = -EACCES;
+ }
+ else
+ {
+ error = 0;
+ ncp_invalid_dir_cache(dir);
+ }
+
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_rmdir(struct inode *dir, const char *name, int len)
+{
+ int error;
+ __u8 _name[len+1];
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_rmdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+ if (ncp_find_dir_inode(dir, name) != NULL)
+ {
+ iput(dir);
+ error = -EBUSY;
+ }
+ else
+ {
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir),
+ _name)) == 0)
+ {
+ ncp_invalid_dir_cache(dir);
+ }
+ else
+ {
+ error = -EACCES;
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_unlink(struct inode *dir, const char *name, int len)
+{
+ int error;
+ __u8 _name[len+1];
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_unlink: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+ if (ncp_find_dir_inode(dir, name) != NULL)
+ {
+ iput(dir);
+ error = -EBUSY;
+ }
+ else
+ {
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir),
+ _name)) == 0)
+ {
+ ncp_invalid_dir_cache(dir);
+ }
+ else
+ {
+ error = -EACCES;
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_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 res;
+ char _old_name[old_len+1];
+ char _new_name[new_len+1];
+
+ if (!old_dir || !S_ISDIR(old_dir->i_mode))
+ {
+ printk("ncp_rename: old inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+
+ if (!ncp_conn_valid(NCP_SERVER(old_dir)))
+ {
+ res = -EIO;
+ goto finished;
+ }
+
+ if (!new_dir || !S_ISDIR(new_dir->i_mode))
+ {
+ printk("ncp_rename: new inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+
+ if ( (ncp_find_dir_inode(old_dir, old_name) != NULL)
+ || (ncp_find_dir_inode(new_dir, new_name) != NULL))
+ {
+ res = -EBUSY;
+ goto finished;
+ }
+
+ strncpy(_old_name, old_name, old_len);
+ _old_name[old_len] = '\0';
+
+ if (!ncp_preserve_case(old_dir))
+ {
+ str_upper(_old_name);
+ }
+
+ strncpy(_new_name, new_name, new_len);
+ _new_name[new_len] = '\0';
+
+ if (!ncp_preserve_case(new_dir))
+ {
+ str_upper(_new_name);
+ }
+
+ res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ NCP_ISTRUCT(old_dir), _old_name,
+ NCP_ISTRUCT(new_dir), _new_name);
+
+ if (res == 0)
+ {
+ ncp_invalid_dir_cache(old_dir);
+ ncp_invalid_dir_cache(new_dir);
+ }
+ else
+ {
+ res = -EACCES;
+ }
+
+ finished:
+ iput(old_dir);
+ iput(new_dir);
+ return res;
+}
+
+/* The following routines are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int
+utc2local(int time)
+{
+ return time - sys_tz.tz_minuteswest*60 +
+ (sys_tz.tz_dsttime ? 3600 : 0);
+}
+
+static int
+local2utc(int time)
+{
+ return time + sys_tz.tz_minuteswest*60 -
+ (sys_tz.tz_dsttime ? 3600 : 0);
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+int
+ncp_date_dos2unix(unsigned short time,unsigned short date)
+{
+ int month,year,secs;
+
+ month = ((date >> 5) & 15)-1;
+ year = date >> 9;
+ secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+ ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+ month < 2 ? 1 : 0)+3653);
+ /* days since 1.1.70 plus 80's leap day */
+ return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+void
+ncp_date_unix2dos(int unix_date,unsigned short *time, unsigned short *date)
+{
+ int day,year,nl_day,month;
+
+ unix_date = utc2local(unix_date);
+ *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+ (((unix_date/3600) % 24) << 11);
+ day = unix_date/86400-3652;
+ year = day/365;
+ if ((year+3)/4+365*year > day) year--;
+ day -= (year+3)/4+365*year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ }
+ else {
+ nl_day = (year & 3) || day <= 59 ? day : day-1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day) break;
+ }
+ *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
new file mode 100644
index 000000000..12b646f91
--- /dev/null
+++ b/fs/ncpfs/file.c
@@ -0,0 +1,281 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <asm/uaccess.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/ncp_fs.h>
+#include <linux/locks.h>
+#include "ncplib_kernel.h"
+#include <linux/malloc.h>
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+static int
+ncp_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int
+ncp_make_open(struct inode *i, int right)
+{
+ struct nw_file_info *finfo;
+
+ if (i == NULL)
+ {
+ printk("ncp_make_open: got NULL inode\n");
+ return -EINVAL;
+ }
+
+ finfo = NCP_FINFO(i);
+
+ DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened);
+
+ lock_super(i->i_sb);
+ if (finfo->opened == 0)
+ {
+ finfo->access = -1;
+ /* tries max. rights */
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
+ NULL, NULL,
+ OC_MODE_OPEN, 0,
+ AR_READ | AR_WRITE,
+ finfo) == 0)
+ {
+ finfo->access = O_RDWR;
+ }
+ else if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
+ NULL, NULL,
+ OC_MODE_OPEN, 0,
+ AR_READ,
+ finfo) == 0)
+ {
+ finfo->access = O_RDONLY;
+ }
+ }
+
+ unlock_super(i->i_sb);
+
+ if ( ((right == O_RDONLY) && ( (finfo->access == O_RDONLY)
+ || (finfo->access == O_RDWR)))
+ || ((right == O_WRONLY) && ( (finfo->access == O_WRONLY)
+ || (finfo->access == O_RDWR)))
+ || ((right == O_RDWR) && (finfo->access == O_RDWR)))
+ return 0;
+
+ return -EACCES;
+}
+
+static long
+ncp_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int bufsize, already_read;
+ off_t pos;
+ int errno;
+
+ DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ if (inode == NULL)
+ {
+ DPRINTK("ncp_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("ncp_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;
+ }
+
+ if ((errno = ncp_make_open(inode, O_RDONLY)) != 0)
+ {
+ return errno;
+ }
+
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_read = 0;
+
+ /* First read in as much as possible for each bufsize. */
+ while (already_read < count)
+ {
+ int read_this_time;
+ int to_read = min(bufsize - (pos % bufsize),
+ count - already_read);
+
+ if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ pos, to_read, buf, &read_this_time) != 0)
+ {
+ return -EIO; /* This is not exact, i know.. */
+ }
+
+ pos += read_this_time;
+ buf += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time < to_read)
+ {
+ break;
+ }
+ }
+
+ file->f_pos = pos;
+
+ if (!IS_RDONLY(inode))
+ {
+ inode->i_atime = CURRENT_TIME;
+ }
+
+ inode->i_dirt = 1;
+
+ DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ return already_read;
+}
+
+static long
+ncp_file_write(struct inode *inode, struct file *file, const char *buf,
+ unsigned long count)
+{
+ int bufsize, already_written;
+ off_t pos;
+ int errno;
+
+ if (inode == NULL)
+ {
+ DPRINTK("ncp_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("ncp_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+
+ DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+
+ if ((errno = ncp_make_open(inode, O_RDWR)) != 0)
+ {
+ return errno;
+ }
+
+ pos = file->f_pos;
+
+ if (file->f_flags & O_APPEND)
+ {
+ pos = inode->i_size;
+ }
+
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_written = 0;
+
+ while (already_written < count)
+ {
+ int written_this_time;
+ int to_write = min(bufsize - (pos % bufsize),
+ count - already_written);
+
+ if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ pos, to_write, buf, &written_this_time) != 0)
+ {
+ return -EIO;
+ }
+
+ pos += written_this_time;
+ buf += written_this_time;
+ already_written += written_this_time;
+
+ if (written_this_time < to_write)
+ {
+ break;
+ }
+ }
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ file->f_pos = pos;
+
+ if (pos > inode->i_size)
+ {
+ inode->i_size = pos;
+ ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+ }
+
+ DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ return already_written;
+}
+
+static struct file_operations ncp_file_operations = {
+ NULL, /* lseek - default */
+ ncp_file_read, /* read */
+ ncp_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ ncp_ioctl, /* ioctl */
+ ncp_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ ncp_fsync, /* fsync */
+};
+
+struct inode_operations ncp_file_inode_operations = {
+ &ncp_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 */
+};
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
new file mode 100644
index 000000000..55a0c4aae
--- /dev/null
+++ b/fs/ncpfs/inode.c
@@ -0,0 +1,570 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/sched.h>
+#include <linux/ncp_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>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include "ncplib_kernel.h"
+
+extern int close_fp(struct file *filp);
+
+static void ncp_put_inode(struct inode *);
+static void ncp_read_inode(struct inode *);
+static void ncp_put_super(struct super_block *);
+static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int ncp_notify_change(struct inode *inode, struct iattr *attr);
+
+static struct super_operations ncp_sops = {
+ ncp_read_inode, /* read inode */
+ ncp_notify_change, /* notify change */
+ NULL, /* write inode */
+ ncp_put_inode, /* put inode */
+ ncp_put_super, /* put superblock */
+ NULL, /* write superblock */
+ ncp_statfs, /* stat filesystem */
+ NULL
+};
+
+/* ncp_read_inode: Called from iget, it only traverses the allocated
+ ncp_inode_info's and initializes the inode from the data found
+ there. It does not allocate or deallocate anything. */
+
+static void
+ncp_read_inode(struct inode *inode)
+{
+ /* Our task should be extremely simple here. We only have to
+ look up the information somebody else (ncp_iget) put into
+ the inode tree. The address of this information is the
+ inode->i_ino. Just to make sure everything went well, we
+ check it's there. */
+
+ struct ncp_inode_info *inode_info = ncp_find_inode(inode);
+
+ if (inode_info == NULL)
+ {
+ /* Ok, now we're in trouble. The inode info is not there. What
+ should we do now??? */
+ printk("ncp_read_inode: inode info not found\n");
+ return;
+ }
+
+ inode_info->state = NCP_INODE_VALID;
+
+ NCP_INOP(inode) = inode_info;
+ inode_info->inode = inode;
+
+ if (NCP_ISTRUCT(inode)->attributes & aDIR)
+ {
+ inode->i_mode = NCP_SERVER(inode)->m.dir_mode;
+ /* for directories dataStreamSize seems to be some
+ Object ID ??? */
+ inode->i_size = 512;
+ }
+ else
+ {
+ inode->i_mode = NCP_SERVER(inode)->m.file_mode;
+ inode->i_size = NCP_ISTRUCT(inode)->dataStreamSize;
+ }
+
+ DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
+
+ inode->i_nlink = 1;
+ inode->i_uid = NCP_SERVER(inode)->m.uid;
+ inode->i_gid = NCP_SERVER(inode)->m.gid;
+ inode->i_blksize = 512;
+ inode->i_rdev = 0;
+
+ if ((inode->i_blksize != 0) && (inode->i_size != 0))
+ {
+ inode->i_blocks =
+ (inode->i_size - 1) / inode->i_blksize + 1;
+ }
+ else
+ {
+ inode->i_blocks = 0;
+ }
+
+ inode->i_mtime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->modifyTime,
+ NCP_ISTRUCT(inode)->modifyDate);
+ inode->i_ctime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->creationTime,
+ NCP_ISTRUCT(inode)->creationDate);
+ inode->i_atime = ncp_date_dos2unix(0,
+ NCP_ISTRUCT(inode)->lastAccessDate);
+
+ if (S_ISREG(inode->i_mode))
+ {
+ inode->i_op = &ncp_file_inode_operations;
+ }
+ else if (S_ISDIR(inode->i_mode))
+ {
+ inode->i_op = &ncp_dir_inode_operations;
+ }
+ else
+ {
+ inode->i_op = NULL;
+ }
+}
+
+static void
+ncp_put_inode(struct inode *inode)
+{
+ struct nw_file_info *finfo = NCP_FINFO(inode);
+ struct super_block *sb = inode->i_sb;
+
+ lock_super(sb);
+ if (finfo->opened != 0)
+ {
+ if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle)!=0)
+ {
+ /* We can't do anything but complain. */
+ printk("ncp_put_inode: could not close\n");
+ }
+ }
+
+ DDPRINTK("ncp_put_inode: put %s\n",
+ finfo->i.entryName);
+
+ ncp_free_inode_info(NCP_INOP(inode));
+
+ if (S_ISDIR(inode->i_mode))
+ {
+ DDPRINTK("ncp_put_inode: put directory %ld\n",
+ inode->i_ino);
+ ncp_invalid_dir_cache(inode);
+ }
+
+ clear_inode(inode);
+ unlock_super(sb);
+}
+
+struct super_block *
+ncp_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data;
+ struct ncp_server *server;
+ struct file *ncp_filp;
+ struct file *wdog_filp;
+ struct file *msg_filp;
+ kdev_t dev = sb->s_dev;
+ int error;
+
+ if (data == NULL)
+ {
+ printk("ncp_read_super: missing data argument\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if (data->version != NCP_MOUNT_VERSION)
+ {
+ printk("ncp warning: mount version %s than kernel\n",
+ (data->version < NCP_MOUNT_VERSION) ?
+ "older" : "newer");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->ncp_fd >= NR_OPEN)
+ || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL)
+ || (!S_ISSOCK(ncp_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid ncp socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->wdog_fd >= NR_OPEN)
+ || ((wdog_filp = current->files->fd[data->wdog_fd]) == NULL)
+ || (!S_ISSOCK(wdog_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid wdog socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->message_fd >= NR_OPEN)
+ || ((msg_filp = current->files->fd[data->message_fd]) == NULL)
+ || (!S_ISSOCK(msg_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid wdog socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ /* We must malloc our own super-block info */
+ server = (struct ncp_server *)ncp_kmalloc(sizeof(struct ncp_server),
+ GFP_KERNEL);
+
+ if (server == NULL)
+ {
+ printk("ncp_read_super: could not alloc ncp_server\n");
+ return NULL;
+ }
+
+ ncp_filp->f_count += 1;
+ wdog_filp->f_count += 1;
+ msg_filp->f_count += 1;
+
+ lock_super(sb);
+
+ NCP_SBP(sb) = server;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = NCP_SUPER_MAGIC;
+ sb->s_dev = dev;
+ sb->s_op = &ncp_sops;
+
+ server->ncp_filp = ncp_filp;
+ server->wdog_filp = wdog_filp;
+ server->msg_filp = msg_filp;
+ server->lock = 0;
+ server->wait = NULL;
+ server->packet = NULL;
+ server->buffer_size = 0;
+ server->conn_status = 0;
+
+ server->m = *data;
+ server->m.file_mode = (server->m.file_mode &
+ (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG;
+ server->m.dir_mode = (server->m.dir_mode &
+ (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR;
+
+ /* protect against invalid mount points */
+ server->m.mount_point[sizeof(server->m.mount_point)-1] = '\0';
+
+ server->packet_size = NCP_PACKET_SIZE;
+ server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL);
+
+ if (server->packet == NULL)
+ {
+ printk("ncpfs: could not alloc packet\n");
+ error = -ENOMEM;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ /*
+ * Make the connection to the server
+ */
+
+ if (ncp_catch_watchdog(server) != 0)
+ {
+ printk("ncp_read_super: Could not catch watchdog\n");
+ error = -EINVAL;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ if (ncp_catch_message(server) != 0)
+ {
+ printk("ncp_read_super: Could not catch messages\n");
+ ncp_dont_catch_watchdog(server);
+ error = -EINVAL;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ ncp_lock_server(server);
+ error = ncp_connect(server);
+ ncp_unlock_server(server);
+ unlock_super(sb);
+
+ if (error < 0)
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: Failed connection, bailing out "
+ "(error = %d).\n", -error);
+ ncp_kfree_s(server->packet, server->packet_size);
+ ncp_dont_catch_watchdog(server);
+ goto fail;
+ }
+
+ DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int)NCP_SBP(sb));
+
+ ncp_init_root(server);
+
+ if (!(sb->s_mounted = iget(sb, ncp_info_ino(server, &(server->root)))))
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: get root inode failed\n");
+ goto disconnect;
+ }
+
+ if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
+ &(server->buffer_size)) != 0)
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: could not get bufsize\n");
+ goto disconnect;
+ }
+
+ DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
+
+ MOD_INC_USE_COUNT;
+ return sb;
+
+ disconnect:
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+ ncp_kfree_s(server->packet, server->packet_size);
+ ncp_dont_catch_watchdog(server);
+ fail:
+ ncp_filp->f_count -= 1;
+ wdog_filp->f_count -= 1;
+ msg_filp->f_count -= 1;
+ ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
+ return NULL;
+}
+
+static void
+ncp_put_super(struct super_block *sb)
+{
+ struct ncp_server *server = NCP_SBP(sb);
+
+ lock_super(sb);
+
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+
+ close_fp(server->ncp_filp);
+
+ ncp_dont_catch_watchdog(server);
+ close_fp(server->wdog_filp);
+ close_fp(server->msg_filp);
+
+ ncp_free_all_inodes(server);
+
+ ncp_kfree_s(server->packet, server->packet_size);
+
+ sb->s_dev = 0;
+ ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
+ NCP_SBP(sb) = NULL;
+
+ unlock_super(sb);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* This routine is called from an interrupt in ncp_msg_data_ready. So
+ * we have to be careful NOT to sleep here! */
+void
+ncp_trigger_message(struct ncp_server *server)
+{
+#ifdef CONFIG_KERNELD
+ char command[ sizeof(server->m.mount_point)
+ + sizeof(NCP_MSG_COMMAND) + 2];
+#endif
+
+ if (server == NULL)
+ {
+ printk("ncp_trigger_message: invalid server!\n");
+ return;
+ }
+
+ DPRINTK("ncp_trigger_message: on %s\n",
+ server->m.mount_point);
+
+#ifdef CONFIG_KERNELD
+ strcpy(command, NCP_MSG_COMMAND);
+ strcat(command, " ");
+ strcat(command, server->m.mount_point);
+ DPRINTK("ksystem: %s\n", command);
+ ksystem(command, KERNELD_NOWAIT);
+#endif
+}
+
+static void
+ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+
+ /* We cannot say how much disk space is left on a mounted
+ NetWare Server, because free space is distributed over
+ volumes, and the current user might have disk quotas. So
+ free space is not that simple to determine. Our decision
+ here is to err conservatively. */
+
+ tmp.f_type = NCP_SUPER_MAGIC;
+ tmp.f_bsize = 512;
+ tmp.f_blocks = 0;
+ tmp.f_bfree = 0;
+ tmp.f_bavail = 0;
+ tmp.f_files = -1;
+ tmp.f_ffree = -1;
+ tmp.f_namelen = 12;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+static int
+ncp_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int result = 0;
+ int info_mask;
+ struct nw_modify_dos_info info;
+
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if ((result = inode_change_ok(inode, attr)) < 0)
+ return result;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != NCP_SERVER(inode)->m.uid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_uid != NCP_SERVER(inode)->m.gid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode &
+ ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+ return -EPERM;
+
+ info_mask = 0;
+ memset(&info, 0, sizeof(info));
+
+ if ((attr->ia_valid & ATTR_CTIME) != 0)
+ {
+ info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE);
+ ncp_date_unix2dos(attr->ia_ctime,
+ &(info.creationTime), &(info.creationDate));
+ }
+
+ if ((attr->ia_valid & ATTR_MTIME) != 0)
+ {
+ info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE);
+ ncp_date_unix2dos(attr->ia_mtime,
+ &(info.modifyTime), &(info.modifyDate));
+ }
+
+ if ((attr->ia_valid & ATTR_ATIME) != 0)
+ {
+ __u16 dummy;
+ info_mask |= (DM_LAST_ACCESS_DATE);
+ ncp_date_unix2dos(attr->ia_ctime,
+ &(dummy), &(info.lastAccessDate));
+ }
+
+ if (info_mask != 0)
+ {
+ if ((result =
+ ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
+ NCP_ISTRUCT(inode),
+ info_mask,
+ &info)) != 0)
+ {
+ result = -EACCES;
+
+ if (info_mask == (DM_CREATE_TIME|DM_CREATE_DATE))
+ {
+ /* NetWare seems not to allow this. I
+ do not know why. So, just tell the
+ user everything went fine. This is
+ a terrible hack, but I do not know
+ how to do this correctly. */
+ result = 0;
+ }
+ }
+ }
+
+ if ((attr->ia_valid & ATTR_SIZE) != 0)
+ {
+ int written;
+
+ DPRINTK("ncpfs: trying to change size of %s to %ld\n",
+ NCP_ISTRUCT(inode)->entryName, attr->ia_size);
+
+ if ((result = ncp_make_open(inode, O_RDWR)) < 0)
+ {
+ return -EACCES;
+ }
+
+ ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ attr->ia_size, 0, "", &written);
+
+ /* According to ndir, the changes only take effect after
+ closing the file */
+ ncp_close_file(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle);
+ NCP_FINFO(inode)->opened = 0;
+
+ result = 0;
+ }
+
+ ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+
+ return result;
+}
+
+#ifdef DEBUG_NCP_MALLOC
+int ncp_malloced;
+int ncp_current_malloced;
+#endif
+
+static struct file_system_type ncp_fs_type = {
+ ncp_read_super, "ncpfs", 0, NULL
+ };
+
+int init_ncp_fs(void)
+{
+ return register_filesystem(&ncp_fs_type);
+}
+
+#ifdef MODULE
+int
+init_module( void)
+{
+ int status;
+
+ DPRINTK("ncpfs: init_module called\n");
+
+#ifdef DEBUG_NCP_MALLOC
+ ncp_malloced = 0;
+ ncp_current_malloced = 0;
+#endif
+ ncp_init_dir_cache();
+
+ if ((status = init_ncp_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void
+cleanup_module(void)
+{
+ DPRINTK("ncpfs: cleanup_module called\n");
+ ncp_free_dir_cache();
+ unregister_filesystem(&ncp_fs_type);
+#ifdef DEBUG_NCP_MALLOC
+ printk("ncp_malloced: %d\n", ncp_malloced);
+ printk("ncp_current_malloced: %d\n", ncp_current_malloced);
+#endif
+}
+
+#endif
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
new file mode 100644
index 000000000..e7d8aa7db
--- /dev/null
+++ b/fs/ncpfs/ioctl.c
@@ -0,0 +1,160 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ncp_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/ncp.h>
+
+int
+ncp_ioctl (struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int result;
+ struct ncp_ioctl_request request;
+ struct ncp_fs_info info;
+ struct ncp_server *server = NCP_SERVER(inode);
+
+ /*
+ * Binary compatible with 1.3.XX releases.
+ * Take this out in 2.1.0 development series.
+ * <mec@duracef.shout.net> 12 Mar 1996
+ */
+ switch(cmd) {
+ case _IOR('n', 1, unsigned char *):
+ cmd = NCP_IOC_NCPREQUEST;
+ break;
+ case _IOR('u', 1, uid_t):
+ cmd = NCP_IOC_GETMOUNTUID;
+ break;
+ case _IO('l', 1):
+ cmd = NCP_IOC_CONN_LOGGED_IN;
+ break;
+ case _IOWR('i', 1, unsigned char *):
+ cmd = NCP_IOC_GET_FS_INFO;
+ break;
+ }
+
+ switch(cmd) {
+ case NCP_IOC_NCPREQUEST:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_READ, (char *)arg,
+ sizeof(request))) != 0)
+ {
+ return result;
+ }
+
+ copy_from_user(&request, (struct ncp_ioctl_request *)arg,
+ sizeof(request));
+
+ if ( (request.function > 255)
+ || (request.size >
+ NCP_PACKET_SIZE - sizeof(struct ncp_request_header)))
+ {
+ return -EINVAL;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (char *)request.data,
+ NCP_PACKET_SIZE)) != 0)
+ {
+ return result;
+ }
+
+ ncp_lock_server(server);
+
+ /* FIXME: We hack around in the server's structures
+ here to be able to use ncp_request */
+
+ server->has_subfunction = 0;
+ server->current_size = request.size;
+ copy_from_user(server->packet, request.data, request.size);
+
+ ncp_request(server, request.function);
+
+ DPRINTK("ncp_ioctl: copy %d bytes\n",
+ server->reply_size);
+ copy_to_user(request.data, server->packet, server->reply_size);
+
+ ncp_unlock_server(server);
+
+ return server->reply_size;
+
+ case NCP_IOC_CONN_LOGGED_IN:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ return ncp_conn_logged_in(server);
+
+ case NCP_IOC_GET_FS_INFO:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (char *)arg,
+ sizeof(info))) != 0)
+ {
+ return result;
+ }
+
+ copy_from_user(&info, (struct ncp_fs_info *)arg,
+ sizeof(info));
+
+ if (info.version != NCP_GET_FS_INFO_VERSION)
+ {
+ DPRINTK("info.version invalid: %d\n", info.version);
+ return -EINVAL;
+ }
+
+ info.addr = server->m.serv_addr;
+ info.mounted_uid = server->m.mounted_uid;
+ info.connection = server->connection;
+ info.buffer_size = server->buffer_size;
+ info.volume_number = NCP_ISTRUCT(inode)->volNumber;
+ info.directory_id = NCP_ISTRUCT(inode)->DosDirNum;
+
+ copy_to_user((struct ncp_fs_info *)arg, &info, sizeof(info));
+ return 0;
+
+ case NCP_IOC_GETMOUNTUID:
+
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg,
+ sizeof(uid_t))) != 0)
+ {
+ return result;
+ }
+ put_user(server->m.mounted_uid, (uid_t *) arg);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
new file mode 100644
index 000000000..c7b828dc6
--- /dev/null
+++ b/fs/ncpfs/mmap.c
@@ -0,0 +1,155 @@
+/*
+ * mmap.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#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/fcntl.h>
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long
+ncp_file_mmap_nopage(struct vm_area_struct * area,
+ unsigned long address, int no_share)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ unsigned long tmp;
+ int bufsize;
+ int pos;
+ unsigned short fs;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
+ 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;
+ }
+
+ /* what we can read in one go */
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ if (ncp_make_open(inode, O_RDONLY) < 0)
+ {
+ clear = PAGE_SIZE;
+ }
+ else
+ {
+ int already_read = 0;
+ int count = PAGE_SIZE - clear;
+ int to_read;
+
+ while (already_read < count)
+ {
+ int read_this_time;
+
+ if ((pos % bufsize) != 0)
+ {
+ to_read = bufsize - (pos % bufsize);
+ }
+ else
+ {
+ to_read = bufsize;
+ }
+
+ to_read = min(to_read, count - already_read);
+
+ if (ncp_read(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_read,
+ (char *)(page + already_read),
+ &read_this_time) != 0)
+ {
+ read_this_time = 0;
+ }
+
+ pos += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time < to_read)
+ {
+ break;
+ }
+ }
+
+ }
+
+ set_fs(fs);
+
+ tmp = page + PAGE_SIZE;
+ while (clear--) {
+ *(char *)--tmp = 0;
+ }
+ return page;
+}
+
+struct vm_operations_struct ncp_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ ncp_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+
+/* This is used for a general mmap of a ncp file */
+int
+ncp_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+ DPRINTK("ncp_mmap: called\n");
+
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ /* only PAGE_COW or read-only supported now */
+ if (vma->vm_flags & VM_SHARED)
+ 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 = &ncp_file_mmap;
+ return 0;
+}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
new file mode 100644
index 000000000..a0c6f5650
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -0,0 +1,627 @@
+/*
+ * ncplib_kernel.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include "ncplib_kernel.h"
+
+typedef __u8 byte;
+typedef __u16 word;
+typedef __u32 dword;
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+static void
+assert_server_locked(struct ncp_server *server)
+{
+ if (server->lock == 0)
+ {
+ DPRINTK("ncpfs: server not locked!\n");
+ }
+}
+
+static void
+ncp_add_byte(struct ncp_server *server, byte x)
+{
+ assert_server_locked(server);
+ *(byte *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 1;
+ return;
+}
+
+static void
+ncp_add_word(struct ncp_server *server, word x)
+{
+ assert_server_locked(server);
+ *(word *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 2;
+ return;
+}
+
+static void
+ncp_add_dword(struct ncp_server *server, dword x)
+{
+ assert_server_locked(server);
+ *(dword *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 4;
+ return;
+}
+
+static void
+ncp_add_mem(struct ncp_server *server, const void *source, int size)
+{
+ assert_server_locked(server);
+ memcpy(&(server->packet[server->current_size]), source, size);
+ server->current_size += size;
+ return;
+}
+
+static void
+ncp_add_mem_fromfs(struct ncp_server *server, const char *source, int size)
+{
+ assert_server_locked(server);
+ copy_from_user(&(server->packet[server->current_size]), source, size);
+ server->current_size += size;
+ return;
+}
+
+static void
+ncp_add_pstring(struct ncp_server *server, const char *s)
+{
+ int len = strlen(s);
+ assert_server_locked(server);
+ if (len > 255)
+ {
+ DPRINTK("ncpfs: string too long: %s\n", s);
+ len = 255;
+ }
+ ncp_add_byte(server, len);
+ ncp_add_mem(server, s, len);
+ return;
+}
+
+static void
+ncp_init_request(struct ncp_server *server)
+{
+ ncp_lock_server(server);
+
+ server->current_size = sizeof(struct ncp_request_header);
+ server->has_subfunction = 0;
+}
+
+static void
+ncp_init_request_s(struct ncp_server *server, int subfunction)
+{
+ ncp_init_request(server);
+ ncp_add_word(server, 0); /* preliminary size */
+
+ ncp_add_byte(server, subfunction);
+
+ server->has_subfunction = 1;
+}
+
+static char *
+ncp_reply_data(struct ncp_server *server, int offset)
+{
+ return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
+}
+
+static byte
+ncp_reply_byte(struct ncp_server *server, int offset)
+{
+ return *(byte *)(ncp_reply_data(server, offset));
+}
+
+static word
+ncp_reply_word(struct ncp_server *server, int offset)
+{
+ return *(word *)(ncp_reply_data(server, offset));
+}
+
+static dword
+ncp_reply_dword(struct ncp_server *server, int offset)
+{
+ return *(dword *)(ncp_reply_data(server, offset));
+}
+
+int
+ncp_negotiate_buffersize(struct ncp_server *server,
+ int size, int *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_word(server, htons(size));
+
+ if ((result = ncp_request(server, 33)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *target =min(ntohs(ncp_reply_word(server, 0)), size);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+ struct ncp_volume_info *target)
+{
+ int result;
+ int len;
+
+ ncp_init_request_s(server, 44);
+ ncp_add_byte(server, n);
+
+ if ((result = ncp_request(server, 22)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ target->total_blocks = ncp_reply_dword(server, 0);
+ target->free_blocks = ncp_reply_dword(server, 4);
+ target->purgeable_blocks = ncp_reply_dword(server, 8);
+ target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12);
+ target->total_dir_entries = ncp_reply_dword(server, 16);
+ target->available_dir_entries = ncp_reply_dword(server, 20);
+ target->sectors_per_block = ncp_reply_byte(server, 28);
+
+ memset(&(target->volume_name), 0, sizeof(target->volume_name));
+
+ len = ncp_reply_byte(server, 29);
+ if (len > NCP_VOLNAME_LEN)
+ {
+ DPRINTK("ncpfs: volume name too long: %d\n", len);
+ ncp_unlock_server(server);
+ return -EIO;
+ }
+
+ memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_close_file(struct ncp_server *server, const char *file_id)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+
+ result = ncp_request(server, 66);
+ ncp_unlock_server(server);
+ return result;
+}
+
+static void
+ncp_add_handle_path(struct ncp_server *server,
+ __u8 vol_num,
+ __u32 dir_base, int have_dir_base,
+ char *path)
+{
+ ncp_add_byte(server, vol_num);
+ ncp_add_dword(server, dir_base);
+ if (have_dir_base != 0)
+ {
+ ncp_add_byte(server, 1); /* dir_base */
+ }
+ else
+ {
+ ncp_add_byte(server, 0xff); /* no handle */
+ }
+ if (path != NULL)
+ {
+ ncp_add_byte(server, 1); /* 1 component */
+ ncp_add_pstring(server, path);
+ }
+ else
+ {
+ ncp_add_byte(server, 0);
+ }
+}
+
+static void
+ncp_extract_file_info(void *structure, struct nw_info_struct *target)
+{
+ __u8 *name_len;
+ const int info_struct_size = sizeof(struct nw_info_struct) - 257;
+
+ memcpy(target, structure, info_struct_size);
+ name_len = structure + info_struct_size;
+ target->nameLen = *name_len;
+ strncpy(target->entryName, name_len+1, *name_len);
+ target->entryName[*name_len] = '\0';
+ return;
+}
+
+int
+ncp_obtain_info(struct ncp_server *server,
+ __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ struct nw_info_struct *target)
+{
+ int result;
+
+ if (target == NULL)
+ {
+ return -EINVAL;
+ }
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, server->name_space[vol_num]);
+ ncp_add_byte(server, server->name_space[vol_num]);
+ ncp_add_word(server, 0xff); /* get all */
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ ncp_extract_file_info(ncp_reply_data(server, 0), target);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+static inline int
+ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+{
+ int result;
+ __u8 *namespace;
+ __u16 no_namespaces;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */
+ ncp_add_word(server, 0);
+ ncp_add_byte(server, volume);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return 0;
+ }
+
+ no_namespaces = ncp_reply_word(server, 0);
+ namespace = ncp_reply_data(server, 2);
+
+ while (no_namespaces > 0)
+ {
+ DPRINTK("get_namespaces: found %d on %d\n", *namespace,volume);
+
+ if (*namespace == 4)
+ {
+ DPRINTK("get_namespaces: found OS2\n");
+ ncp_unlock_server(server);
+ return 1;
+ }
+ namespace += 1;
+ no_namespaces -= 1;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_lookup_volume(struct ncp_server *server,
+ char *volname,
+ struct nw_info_struct *target)
+{
+ int result;
+ int volnum;
+
+ DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname);
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */
+ ncp_add_byte(server, 0); /* DOS namespace */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+
+ ncp_add_byte(server, 0); /* faked volume number */
+ ncp_add_dword(server, 0); /* faked dir_base */
+ ncp_add_byte(server, 0xff); /* Don't have a dir_base */
+ ncp_add_byte(server, 1); /* 1 path component */
+ ncp_add_pstring(server, volname);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memset(target, 0, sizeof(*target));
+ target->DosDirNum = target->dirEntNum = ncp_reply_dword(server, 4);
+ target->volNumber = volnum = ncp_reply_byte(server, 8);
+ ncp_unlock_server(server);
+
+ server->name_space[volnum] = ncp_has_os2_namespace(server,volnum)?4:0;
+
+ DPRINTK("lookup_vol: namespace[%d] = %d\n",
+ volnum, server->name_space[volnum]);
+
+ target->nameLen = strlen(volname);
+ strcpy(target->entryName, volname);
+ target->attributes = aDIR;
+ return 0;
+}
+
+int
+ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct nw_info_struct *file,
+ __u32 info_mask,
+ struct nw_modify_dos_info *info)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 7); /* subfunction */
+ ncp_add_byte(server, server->name_space[file->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, 0x8006); /* search attribs: all */
+
+ ncp_add_dword(server, info_mask);
+ ncp_add_mem(server, info, sizeof(*info));
+ ncp_add_handle_path(server, file->volNumber,
+ file->dirEntNum, 1, NULL);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 8); /* subfunction */
+ ncp_add_byte(server, server->name_space[dir->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, 0x8006); /* search attribs: all */
+ ncp_add_handle_path(server, dir->volNumber,
+ dir->dirEntNum, 1, name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+static inline void
+ConvertToNWfromDWORD ( __u32 sfd , __u8 ret[6] )
+{
+ __u16 *dest = (__u16 *) ret;
+ memcpy(&(dest[1]), &sfd, 4);
+ dest[0] = dest[1] + 1;
+ return;
+}
+
+/* If both dir and name are NULL, then in target there's already a
+ looked-up entry that wants to be opened. */
+int
+ncp_open_create_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name,
+ int open_create_mode,
+ __u32 create_attributes,
+ int desired_acc_rights,
+ struct nw_file_info *target)
+{
+ int result;
+ __u16 search_attribs = 0x0006;
+ __u8 volume = (dir != NULL) ? dir->volNumber : target->i.volNumber;
+
+ if ((create_attributes & aDIR) != 0)
+ {
+ search_attribs |= 0x8000;
+}
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 1); /* subfunction */
+ ncp_add_byte(server, server->name_space[volume]);
+ ncp_add_byte(server, open_create_mode);
+ ncp_add_word(server, search_attribs);
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_dword(server, create_attributes);
+ /* The desired acc rights seem to be the inherited rights mask
+ for directories */
+ ncp_add_word(server, desired_acc_rights);
+
+ if (dir != NULL)
+ {
+ ncp_add_handle_path(server, volume, dir->dirEntNum, 1, name);
+ }
+ else
+ {
+ ncp_add_handle_path(server, volume, target->i.dirEntNum,
+ 1, NULL);
+ }
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ target->opened = 1;
+ target->server_file_handle = ncp_reply_dword(server, 0);
+ target->open_create_action = ncp_reply_byte(server, 4);
+
+ if (dir != NULL)
+ {
+ /* in target there's a new finfo to fill */
+ ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i));
+ }
+
+ ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+
+int
+ncp_initialize_search(struct ncp_server *server,
+ struct nw_info_struct *dir,
+ struct nw_search_sequence *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 2); /* subfunction */
+ ncp_add_byte(server, server->name_space[dir->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_handle_path(server, dir->volNumber, dir->dirEntNum, 1, NULL);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+/* Search for everything */
+int
+ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 3); /* subfunction */
+ ncp_add_byte(server, server->name_space[seq->volNumber]);
+ ncp_add_byte(server, 0); /* data stream (???) */
+ ncp_add_word(server, 0xffff); /* Search attribs */
+ ncp_add_dword(server, RIM_ALL); /* return info mask */
+ ncp_add_mem(server, seq, 9);
+ ncp_add_byte(server, 2); /* 2 byte pattern */
+ ncp_add_byte(server, 0xff); /* following is a wildcard */
+ ncp_add_byte(server, '*');
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
+ ncp_extract_file_info(ncp_reply_data(server, 10), target);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *old_dir, char *old_name,
+ struct nw_info_struct *new_dir, char *new_name)
+{
+ int result;
+
+ if ( (old_dir == NULL) || (old_name == NULL)
+ || (new_dir == NULL) || (new_name == NULL))
+ return -EINVAL;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 4); /* subfunction */
+ ncp_add_byte(server, server->name_space[old_dir->volNumber]);
+ ncp_add_byte(server, 1); /* rename flag */
+ ncp_add_word(server, 0x8006); /* search attributes */
+
+ /* source Handle Path */
+ ncp_add_byte(server, old_dir->volNumber);
+ ncp_add_dword(server, old_dir->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 source component */
+
+ /* dest Handle Path */
+ ncp_add_byte(server, new_dir->volNumber);
+ ncp_add_dword(server, new_dir->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 destination component */
+
+ /* source path string */
+ ncp_add_pstring(server, old_name);
+ /* dest path string */
+ ncp_add_pstring(server, new_name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+
+/* We have to transfer to/from user space */
+int
+ncp_read(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read,
+ char *target, int *bytes_read)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_read));
+
+ if ((result = ncp_request(server, 72)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *bytes_read = ntohs(ncp_reply_word(server, 0));
+
+ copy_to_user(target, ncp_reply_data(server, 2+(offset&1)), *bytes_read);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_write(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_write));
+ ncp_add_mem_fromfs(server, source, to_write);
+
+ if ((result = ncp_request(server, 73)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *bytes_written = to_write;
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
new file mode 100644
index 000000000..f76c2c666
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -0,0 +1,169 @@
+/*
+ * ncplib_kernel.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#ifndef _NCPLIB_H
+#define _NCPLIB_H
+
+#include <linux/fs.h>
+#include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+
+#include <linux/ncp.h>
+
+int
+ncp_negotiate_buffersize(struct ncp_server *server, int size,
+ int *target);
+int
+ncp_get_encryption_key(struct ncp_server *server,
+ char *target);
+int
+ncp_get_bindery_object_id(struct ncp_server *server,
+ int object_type, char *object_name,
+ struct ncp_bindery_object *target);
+int
+ncp_login_encrypted(struct ncp_server *server,
+ struct ncp_bindery_object *object,
+ unsigned char *key,
+ unsigned char *passwd);
+int
+ncp_login_user(struct ncp_server *server,
+ unsigned char *username,
+ unsigned char *password);
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+ struct ncp_volume_info *target);
+
+int
+ncp_get_volume_number(struct ncp_server *server, const char *name,
+ int *target);
+
+int
+ncp_file_search_init(struct ncp_server *server,
+ int dir_handle, const char *path,
+ struct ncp_filesearch_info *target);
+
+int
+ncp_file_search_continue(struct ncp_server *server,
+ struct ncp_filesearch_info *fsinfo,
+ int attributes, const char *path,
+ struct ncp_file_info *target);
+
+int
+ncp_get_finfo(struct ncp_server *server,
+ int dir_handle, const char *path, const char *name,
+ struct ncp_file_info *target);
+
+int
+ncp_open_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr, int access,
+ struct ncp_file_info *target);
+int
+ncp_close_file(struct ncp_server *server, const char *file_id);
+
+int
+ncp_create_newfile(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr,
+ struct ncp_file_info *target);
+
+int
+ncp_create_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr,
+ struct ncp_file_info *target);
+
+int
+ncp_erase_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr);
+
+int
+ncp_rename_file(struct ncp_server *server,
+ int old_handle, const char *old_path,
+ int attr,
+ int new_handle, const char *new_path);
+
+int
+ncp_create_directory(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int inherit_mask);
+
+int
+ncp_delete_directory(struct ncp_server *server,
+ int dir_handle, const char *path);
+
+int
+ncp_rename_directory(struct ncp_server *server,
+ int dir_handle,
+ const char *old_path, const char *new_path);
+
+int
+ncp_read(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read,
+ char *target, int *bytes_read);
+
+int
+ncp_write(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written);
+
+int
+ncp_obtain_info(struct ncp_server *server,
+ __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ struct nw_info_struct *target);
+
+int
+ncp_lookup_volume(struct ncp_server *server,
+ char *volname,
+ struct nw_info_struct *target);
+
+
+int
+ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct nw_info_struct *file,
+ __u32 info_mask,
+ struct nw_modify_dos_info *info);
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name);
+
+int
+ncp_open_create_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name,
+ int open_create_mode,
+ __u32 create_attributes,
+ int desired_acc_rights,
+ struct nw_file_info *target);
+
+int
+ncp_initialize_search(struct ncp_server *server,
+ struct nw_info_struct *dir,
+ struct nw_search_sequence *target);
+
+int
+ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target);
+
+int
+ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *old_dir, char *old_name,
+ struct nw_info_struct *new_dir, char *new_name);
+
+
+#endif /* _NCPLIB_H */
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
new file mode 100644
index 000000000..b8017a9d2
--- /dev/null
+++ b/fs/ncpfs/sock.c
@@ -0,0 +1,704 @@
+/*
+ * linux/fs/ncpfs/sock.c
+ *
+ * Copyright (C) 1992, 1993 Rick Sladkey
+ *
+ * Modified 1995, 1996 by Volker Lendecke to be usable for ncp
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/ncp_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/ipx.h>
+
+#include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#include <net/sock.h>
+
+
+#define _S(nr) (1<<((nr)-1))
+static int _recvfrom(struct socket *sock, unsigned char *ubuf,
+ int size, int noblock, unsigned flags,
+ struct sockaddr_ipx *sa, int *addr_len)
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = ubuf;
+ iov.iov_len = size;
+
+ msg.msg_name = (void *)sa;
+ msg.msg_namelen = 0;
+ if (addr_len)
+ msg.msg_namelen = *addr_len;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+}
+
+static int _sendto(struct socket *sock, const void *buff,
+ int len, int nonblock, unsigned flags,
+ struct sockaddr_ipx *sa, int addr_len)
+
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = (void *)buff;
+ iov.iov_len = len;
+
+ msg.msg_name = (void *)sa;
+ msg.msg_namelen = addr_len;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+}
+
+
+static void
+ncp_wdog_data_ready(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char packet_buf[2];
+ struct sockaddr_ipx sender;
+ int addr_len = sizeof(struct sockaddr_ipx);
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, &addr_len);
+
+ if ( (result != 2)
+ || (packet_buf[1] != '?')
+ /* How to check connection number here? */
+ )
+ {
+ printk("ncpfs: got strange packet on watchdog "
+ "socket\n");
+ }
+ else
+ {
+ int result;
+ DDPRINTK("ncpfs: got watchdog from:\n");
+ DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
+ " conn:%02X,type:%c\n",
+ htonl(sender.sipx_network),
+ sender.sipx_node[0], sender.sipx_node[1],
+ sender.sipx_node[2], sender.sipx_node[3],
+ sender.sipx_node[4], sender.sipx_node[5],
+ ntohs(sender.sipx_port),
+ packet_buf[0], packet_buf[1]);
+
+ packet_buf[1] = 'Y';
+ result = _sendto(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, sizeof(sender));
+ DDPRINTK("send result: %d\n", result);
+ }
+ set_fs(fs);
+ }
+}
+
+int
+ncp_catch_watchdog(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->wdog_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_catch_watchdog: did not get valid server!\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_catch_watchdog: did not get SOCK_DGRAM\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_catch_watchdog: sk == NULL");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_catch_watchdog: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ if (sk->data_ready == ncp_wdog_data_ready)
+ {
+ printk("ncp_catch_watchdog: already done\n");
+ return -EINVAL;
+ }
+
+ server->data_ready = sk->data_ready;
+ sk->data_ready = ncp_wdog_data_ready;
+ sk->allocation = GFP_ATOMIC;
+ return 0;
+}
+
+int
+ncp_dont_catch_watchdog(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->wdog_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_dont_catch_watchdog: did not get SOCK_DGRAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_dont_catch_watchdog: sk == NULL");
+ return -EINVAL;
+ }
+
+ if (server->data_ready == NULL)
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "server->data_ready == NULL\n");
+ return -EINVAL;
+ }
+
+ if (sk->data_ready != ncp_wdog_data_ready)
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "sk->data_callback != ncp_data_callback\n");
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_dont_catch_watchdog: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ sk->data_ready = server->data_ready;
+ sk->allocation = GFP_KERNEL;
+ server->data_ready = NULL;
+ return 0;
+}
+
+static void
+ncp_msg_data_ready(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char packet_buf[2];
+ struct sockaddr_ipx sender;
+ int addr_len = sizeof(struct sockaddr_ipx);
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, &addr_len);
+
+ DPRINTK("ncpfs: got message of size %d from:\n", result);
+ DPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
+ " conn:%02X,type:%c\n",
+ htonl(sender.sipx_network),
+ sender.sipx_node[0], sender.sipx_node[1],
+ sender.sipx_node[2], sender.sipx_node[3],
+ sender.sipx_node[4], sender.sipx_node[5],
+ ntohs(sender.sipx_port),
+ packet_buf[0], packet_buf[1]);
+
+ ncp_trigger_message(sk->protinfo.af_ipx.ncp_server);
+
+ set_fs(fs);
+ }
+}
+
+int
+ncp_catch_message(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->msg_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_catch_message: did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_catch_message: did not get SOCK_DGRAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_catch_message: sk == NULL");
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_catch_message: sk->d_r = %x\n",
+ (unsigned int)(sk->data_ready));
+
+ if (sk->data_ready == ncp_msg_data_ready)
+ {
+ printk("ncp_catch_message: already done\n");
+ return -EINVAL;
+ }
+
+ sk->data_ready = ncp_msg_data_ready;
+ sk->protinfo.af_ipx.ncp_server = server;
+ return 0;
+}
+
+#define NCP_SLACK_SPACE 1024
+
+#define _S(nr) (1<<((nr)-1))
+
+static int
+do_ncp_rpc_call(struct ncp_server *server, int size)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ unsigned short fs;
+ int result;
+ char *start = server->packet;
+ 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;
+ int acknowledge_seen;
+ char *server_name;
+ int n;
+ int addrlen;
+ unsigned long old_mask;
+
+ /* We have to check the result, so store the complete header */
+ struct ncp_request_header request =
+ *((struct ncp_request_header *)(server->packet));
+
+ struct ncp_reply_header reply;
+
+
+ file = server->ncp_filp;
+ inode = file->f_inode;
+ select = file->f_op->select;
+ sock = &inode->u.socket_i;
+ if (!sock)
+ {
+ printk("ncp_rpc_call: socki_lookup failed\n");
+ return -EBADF;
+ }
+ init_timeout = server->m.time_out;
+ max_timeout = NCP_MAX_RPC_TIMEOUT;
+ retrans = server->m.retry_count;
+ major_timeout_seen = 0;
+ acknowledge_seen = 0;
+ server_name = server->m.server_name;
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL)
+#if 0
+ | _S(SIGSTOP)
+#endif
+ | ((server->m.flags & NCP_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));
+ fs = get_fs();
+ set_fs(get_ds());
+ for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1)
+ {
+ DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+ htonl(server->m.serv_addr.sipx_network),
+ server->m.serv_addr.sipx_node[0],
+ server->m.serv_addr.sipx_node[1],
+ server->m.serv_addr.sipx_node[2],
+ server->m.serv_addr.sipx_node[3],
+ server->m.serv_addr.sipx_node[4],
+ server->m.serv_addr.sipx_node[5],
+ ntohs(server->m.serv_addr.sipx_port));
+ DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+ "seq: %d",
+ request.type,
+ (request.conn_high << 8) + request.conn_low,
+ request.sequence);
+ DDPRINTK(" func: %d\n",
+ request.function);
+
+ result = _sendto(sock, (void *) start, size, 0, 0,
+ &(server->m.serv_addr),
+ sizeof(server->m.serv_addr));
+ if (result < 0)
+ {
+ printk("ncp_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 */
+ if (acknowledge_seen == 0)
+ {
+ printk("NCP 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->m.flags & NCP_MOUNT_SOFT)
+ {
+ printk("NCP 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("NCP 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;
+
+ /* Get the header 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 = _recvfrom(sock, (void *)&reply,
+ sizeof(reply), 1, MSG_PEEK,
+ NULL, &addrlen);
+ if (result < 0)
+ {
+ if (result == -EAGAIN)
+ {
+ DPRINTK("ncp_rpc_call: bad select ready\n");
+ goto re_select;
+ }
+ if (result == -ECONNREFUSED)
+ {
+ DPRINTK("ncp_rpc_call: server playing coy\n");
+ goto re_select;
+ }
+ if (result != -ERESTARTSYS)
+ {
+ printk("ncp_rpc_call: recv error = %d\n",
+ -result);
+ }
+ break;
+ }
+ if ( (result == sizeof(reply))
+ && (reply.type == NCP_POSITIVE_ACK))
+ {
+ /* Throw away the packet */
+ DPRINTK("ncp_rpc_call: got positive acknowledge\n");
+ _recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0,
+ NULL, &addrlen);
+ n = 0;
+ timeout = max_timeout;
+ acknowledge_seen = 1;
+ goto re_select;
+ }
+
+ DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
+ "seq: %d\n",
+ reply.type,
+ (reply.conn_high << 8) + reply.conn_low,
+ reply.task,
+ reply.sequence);
+
+ if ( (result >= sizeof(reply))
+ && (reply.type == NCP_REPLY)
+ && ( (request.type == NCP_ALLOC_SLOT_REQUEST)
+ || ( (reply.sequence == request.sequence)
+ && (reply.conn_low == request.conn_low)
+/* seem to get wrong task from NW311 && (reply.task == request.task)*/
+ && (reply.conn_high == request.conn_high))))
+ {
+ if (major_timeout_seen)
+ printk("NCP 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. */
+ _recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0, NULL,
+ &addrlen);
+
+ DPRINTK("ncp_rpc_call: reply mismatch\n");
+ goto re_select;
+ }
+ /*
+ * we have the correct reply, so read into the correct place and
+ * return it
+ */
+ result = _recvfrom(sock, (void *)start, server->packet_size,
+ 1, 0, NULL, &addrlen);
+ if (result < 0)
+ {
+ printk("NCP: notice message: result=%d\n", result);
+ }
+ else if (result < sizeof(struct ncp_reply_header))
+ {
+ printk("NCP: just caught a too small read memory size..., "
+ "email to NET channel\n");
+ printk("NCP: result=%d,addrlen=%d\n", result, addrlen);
+ result = -EIO;
+ }
+
+ current->blocked = old_mask;
+ set_fs(fs);
+ return result;
+}
+
+
+/*
+ * We need the server to be locked here, so check!
+ */
+
+static int
+ncp_do_request(struct ncp_server *server, int size)
+{
+ int result;
+
+ if (server->lock == 0)
+ {
+ printk("ncpfs: Server not locked!\n");
+ return -EIO;
+ }
+
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+
+ result = do_ncp_rpc_call(server, size);
+
+ DDPRINTK("do_ncp_rpc_call returned %d\n", result);
+
+ if (result < 0)
+ {
+ /* There was a problem with I/O, so the connections is
+ * no longer usable. */
+ ncp_invalidate_conn(server);
+ }
+ return result;
+}
+
+/* ncp_do_request assures that at least a complete reply header is
+ * received. It assumes that server->current_size contains the ncp
+ * request size */
+int
+ncp_request(struct ncp_server *server, int function)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+ struct ncp_reply_header *reply
+ = (struct ncp_reply_header *)(server->packet);
+
+ int request_size = server->current_size
+ - sizeof(struct ncp_request_header);
+
+ int result;
+
+ if (server->has_subfunction != 0)
+ {
+ *(__u16 *)&(h->data[0]) = htons(request_size - 2);
+ }
+
+ h->type = NCP_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ h->task = (current->pid) & 0xff;
+ h->function = function;
+
+ if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0)
+ {
+ DPRINTK("ncp_request_error: %d\n", result);
+ return result;
+ }
+
+ server->completion = reply->completion_code;
+ server->conn_status = reply->connection_state;
+ server->reply_size = result;
+ server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
+
+ result = reply->completion_code;
+
+ if (result != 0)
+ {
+ DPRINTK("ncp_completion_code: %x\n", result);
+ }
+ return result;
+}
+
+int
+ncp_connect(struct ncp_server *server)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+ int result;
+
+ h->type = NCP_ALLOC_SLOT_REQUEST;
+
+ server->sequence = 0;
+ h->sequence = server->sequence;
+ h->conn_low = 0xff;
+ h->conn_high = 0xff;
+ h->task = (current->pid) & 0xff;
+ h->function = 0;
+
+ if ((result = ncp_do_request(server, sizeof(*h))) < 0)
+ {
+ return result;
+ }
+
+ server->sequence = 0;
+ server->connection = h->conn_low + (h->conn_high * 256);
+ return 0;
+}
+
+int
+ncp_disconnect(struct ncp_server *server)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+
+ h->type = NCP_DEALLOC_SLOT_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ h->task = (current->pid) & 0xff;
+ h->function = 0;
+
+ return ncp_do_request(server, sizeof(*h));
+}
+
+void
+ncp_lock_server(struct ncp_server *server)
+{
+#if 0
+ /* For testing, only 1 process */
+ if (server->lock != 0)
+ {
+ DPRINTK("ncpfs: server locked!!!\n");
+ }
+#endif
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+}
+
+void
+ncp_unlock_server(struct ncp_server *server)
+{
+ if (server->lock != 1)
+ {
+ printk("ncp_unlock_server: was not locked!\n");
+ }
+
+ server->lock = 0;
+ wake_up(&server->wait);
+}
+