summaryrefslogtreecommitdiffstats
path: root/fs/smbfs
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/smbfs
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/smbfs')
-rw-r--r--fs/smbfs/Makefile18
-rw-r--r--fs/smbfs/dir.c846
-rw-r--r--fs/smbfs/file.c244
-rw-r--r--fs/smbfs/inode.c474
-rw-r--r--fs/smbfs/ioctl.c29
-rw-r--r--fs/smbfs/mmap.c126
-rw-r--r--fs/smbfs/proc.c1969
-rw-r--r--fs/smbfs/sock.c714
8 files changed, 4420 insertions, 0 deletions
diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
new file mode 100644
index 000000000..0210be26b
--- /dev/null
+++ b/fs/smbfs/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux smb-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 := smbfs.o
+O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o
+M_OBJS := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG_SMB=1 -DDEBUG_SMB_MALLOC=1
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
new file mode 100644
index 000000000..a07d13517
--- /dev/null
+++ b/fs/smbfs/dir.c
@@ -0,0 +1,846 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/errno.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+static long
+ smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
+
+static int
+ smb_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir);
+
+static struct smb_inode_info *
+ smb_find_dir_inode(struct inode *parent, const char *name, int len);
+
+static int
+ smb_lookup(struct inode *dir, const char *__name,
+ int len, struct inode **result);
+
+static int
+ smb_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result);
+
+static int
+ smb_mkdir(struct inode *dir, const char *name, int len, int mode);
+
+static int
+ smb_rmdir(struct inode *dir, const char *name, int len);
+
+static int
+ smb_unlink(struct inode *dir, const char *name, int len);
+
+static int
+ smb_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 struct file_operations smb_dir_operations =
+{
+ NULL, /* lseek - default */
+ smb_dir_read, /* read - bad */
+ NULL, /* write - bad */
+ smb_readdir, /* readdir */
+ NULL, /* select - default */
+ smb_ioctl, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations smb_dir_inode_operations =
+{
+ &smb_dir_operations, /* default directory file ops */
+ smb_create, /* create */
+ smb_lookup, /* lookup */
+ NULL, /* link */
+ smb_unlink, /* unlink */
+ NULL, /* symlink */
+ smb_mkdir, /* mkdir */
+ smb_rmdir, /* rmdir */
+ NULL, /* mknod */
+ smb_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+static int
+strncasecmp(const char *s1, const char *s2, int len)
+{
+ int result = 0;
+
+ for (; len > 0; len -= 1)
+ {
+ char c1, c2;
+
+ c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1;
+ c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2;
+ s1 += 1;
+ s2 += 1;
+
+ if ((result = c1 - c2) != 0 || c1 == 0)
+ {
+ return result;
+ }
+ }
+ return result;
+}
+
+static int
+compare_filename(const struct smb_server *server,
+ const char *s1, int len, struct smb_dirent *entry)
+{
+ if (len != entry->len)
+ {
+ return 1;
+ }
+ if (server->case_handling == CASE_DEFAULT)
+ {
+ return strncasecmp(s1, entry->name, len);
+ }
+ return strncmp(s1, entry->name, len);
+}
+
+struct smb_inode_info *
+smb_find_inode(struct smb_server *server, ino_t ino)
+{
+ struct smb_inode_info *root = &(server->root);
+ struct smb_inode_info *this = root;
+
+ do
+ {
+ if (ino == smb_info_ino(this))
+ {
+ return this;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ return NULL;
+}
+
+static ino_t
+smb_fresh_inodes(struct smb_server *server, int no)
+{
+ static ino_t seed = 1;
+ struct smb_inode_info *root = &(server->root);
+ struct smb_inode_info *this;
+
+ retry:
+ if (seed + no <= no)
+ {
+ /* avoid inode number of 0 at wrap-around */
+ seed += no;
+ }
+ this = root;
+ do
+ {
+ /* We assume that ino_t is unsigned! */
+ if (this->finfo.f_ino - seed < no)
+ {
+ seed += no;
+ goto retry;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ seed += no;
+
+ return seed - no;
+}
+
+static long
+smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+
+static unsigned long c_ino = 0;
+static kdev_t c_dev;
+static int c_size;
+static int c_seen_eof;
+static int c_last_returned_index;
+static struct smb_dirent *c_entry = NULL;
+
+static struct smb_dirent *
+smb_search_in_cache(struct inode *dir, unsigned long f_pos)
+{
+ int i;
+
+ if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
+ {
+ return NULL;
+ }
+ for (i = 0; i < c_size; i++)
+ {
+ if (f_pos == c_entry[i].f_pos)
+ {
+ c_last_returned_index = i;
+ return &(c_entry[i]);
+ }
+ }
+ return NULL;
+}
+
+static int
+smb_refill_dir_cache(struct smb_server *server, struct inode *dir,
+ unsigned long f_pos)
+{
+ int result;
+ static struct semaphore sem = MUTEX;
+ int i;
+ ino_t ino;
+
+ do
+ {
+ down(&sem);
+ result = smb_proc_readdir(server, dir, f_pos,
+ SMB_READDIR_CACHE_SIZE, c_entry);
+
+ if (result <= 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ up(&sem);
+ return result;
+ }
+ c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
+ c_dev = dir->i_dev;
+ c_ino = dir->i_ino;
+ c_size = result;
+ c_last_returned_index = 0;
+
+ ino = smb_fresh_inodes(server, c_size);
+ for (i = 0; i < c_size; i++)
+ {
+ c_entry[i].f_ino = ino;
+ ino += 1;
+ }
+
+ up(&sem);
+
+ }
+ while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
+
+ return result;
+}
+
+static int
+smb_readdir(struct inode *dir, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ int result, i = 0;
+ struct smb_dirent *entry = NULL;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
+ DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
+ dir->i_ino, c_ino);
+
+ if ((dir == NULL) || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_readdir: dir is NULL or not a directory\n");
+ return -EBADF;
+ }
+ if (c_entry == NULL)
+ {
+ i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
+ c_entry = (struct smb_dirent *) smb_vmalloc(i);
+ if (c_entry == NULL)
+ {
+ printk("smb_readdir: no MEMORY for cache\n");
+ return -ENOMEM;
+ }
+ }
+ if (filp->f_pos == 0)
+ {
+ c_ino = 0;
+ c_dev = 0;
+ c_seen_eof = 0;
+
+ if (filldir(dirent, ".", 1, filp->f_pos,
+ smb_info_ino(SMB_INOP(dir))) < 0)
+ {
+ return 0;
+ }
+ filp->f_pos += 1;
+ }
+ if (filp->f_pos == 1)
+ {
+ if (filldir(dirent, "..", 2, filp->f_pos,
+ smb_info_ino(SMB_INOP(dir)->dir)) < 0)
+ {
+ return 0;
+ }
+ filp->f_pos += 1;
+ }
+ entry = smb_search_in_cache(dir, filp->f_pos);
+
+ if (entry == NULL)
+ {
+ if (c_seen_eof)
+ {
+ /* End of directory */
+ return 0;
+ }
+ result = smb_refill_dir_cache(server, dir, filp->f_pos);
+ if (result <= 0)
+ {
+ return result;
+ }
+ entry = c_entry;
+ }
+ while (entry < &(c_entry[c_size]))
+ {
+ /* We found it. 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 smb_inode_info *ino_info
+ = smb_find_dir_inode(dir, entry->name, entry->len);
+
+ ino_t ino = entry->f_ino;
+
+ if (ino_info != NULL)
+ {
+ ino = smb_info_ino(ino_info);
+ }
+ DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
+ DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
+
+ if (filldir(dirent, entry->name, strlen(entry->name),
+ entry->f_pos, ino) < 0)
+ {
+ break;
+ }
+ if ((dir->i_dev != c_dev) || (dir->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;
+ entry += 1;
+ }
+ return 0;
+}
+
+void
+smb_init_dir_cache(void)
+{
+ c_ino = 0;
+ c_dev = 0;
+ c_entry = NULL;
+}
+
+void
+smb_invalid_dir_cache(unsigned long ino)
+{
+ /* TODO: check for dev as well */
+ if (ino == c_ino)
+ {
+ c_ino = 0;
+ c_seen_eof = 0;
+ }
+}
+
+void
+smb_free_dir_cache(void)
+{
+ if (c_entry != NULL)
+ {
+ smb_vfree(c_entry);
+ }
+ c_entry = NULL;
+}
+
+/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
+ under dir. The caller must assure that it's not already there. We
+ assume that path is allocated for us. */
+
+static struct inode *
+smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
+{
+ struct inode *inode;
+ struct smb_inode_info *root;
+
+ if ((dir == NULL) || (new_inode_info == NULL))
+ {
+ printk("smb_iget: parameter is NULL\n");
+ return NULL;
+ }
+ new_inode_info->state = SMB_INODE_LOOKED_UP;
+ new_inode_info->nused = 0;
+ new_inode_info->dir = SMB_INOP(dir);
+
+ SMB_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 = &(SMB_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, smb_info_ino(new_inode_info))))
+ {
+ new_inode_info->next->prev = new_inode_info->prev;
+ new_inode_info->prev->next = new_inode_info->next;
+ SMB_INOP(dir)->nused -= 1;
+
+ printk("smb_iget: iget failed!");
+ return NULL;
+ }
+ return inode;
+}
+
+void
+smb_free_inode_info(struct smb_inode_info *i)
+{
+ if (i == NULL)
+ {
+ printk("smb_free_inode: i == NULL\n");
+ return;
+ }
+ i->state = SMB_INODE_CACHED;
+ while ((i->nused == 0) && (i->state == SMB_INODE_CACHED))
+ {
+ struct smb_inode_info *dir = i->dir;
+
+ i->next->prev = i->prev;
+ i->prev->next = i->next;
+
+ smb_kfree_s(i, sizeof(struct smb_inode_info));
+
+ if (dir == NULL)
+ {
+ return;
+ }
+ dir->nused -= 1;
+ i = dir;
+ }
+}
+
+void
+smb_init_root(struct smb_server *server)
+{
+ struct smb_inode_info *root = &(server->root);
+
+ root->state = SMB_INODE_LOOKED_UP;
+ root->nused = 1;
+ root->dir = NULL;
+ root->next = root->prev = root;
+
+ return;
+}
+
+void
+smb_free_all_inodes(struct smb_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 smb_inode_info *root = &(server->root);
+
+ if (root->next != root)
+ {
+ printk("smb_free_all_inodes: INODES LEFT!!!\n");
+ }
+ while (root->next != root)
+ {
+ printk("smb_free_all_inodes: freeing inode\n");
+ smb_free_inode_info(root->next);
+ /* In case we have an endless loop.. */
+ schedule();
+ }
+#endif
+
+ return;
+}
+
+/* This has to be called when a connection has gone down, so that all
+ file-handles we got from the server are invalid */
+void
+smb_invalidate_all_inodes(struct smb_server *server)
+{
+ struct smb_inode_info *ino = &(server->root);
+
+ do
+ {
+ ino->finfo.opened = 0;
+ ino = ino->next;
+ }
+ while (ino != &(server->root));
+
+ 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 smb_inode_info *
+smb_find_dir_inode(struct inode *parent, const char *name, int len)
+{
+ struct smb_server *server = SMB_SERVER(parent);
+ struct smb_inode_info *dir = SMB_INOP(parent);
+ struct smb_inode_info *result = &(server->root);
+
+ if (name == NULL)
+ {
+ return NULL;
+ }
+ if ((len == 1) && (name[0] == '.'))
+ {
+ return dir;
+ }
+ if ((len == 2) && (name[0] == '.') && (name[1] == '.'))
+ {
+ return dir->dir;
+ }
+ do
+ {
+ if (result->dir == dir)
+ {
+ if (compare_filename(server, name, len,
+ &(result->finfo)) == 0)
+ {
+ return result;
+ }
+ }
+ result = result->next;
+ }
+ while (result != &(server->root));
+
+ return NULL;
+}
+
+static int
+smb_lookup(struct inode *dir, const char *name, int len,
+ struct inode **result)
+{
+ struct smb_dirent finfo;
+ struct smb_inode_info *result_info;
+ int error;
+ int found_in_cache;
+
+ struct smb_inode_info *new_inode_info = NULL;
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_lookup: inode is NULL or not a directory.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ DDPRINTK("smb_lookup: %s\n", name);
+
+ /* Fast cheat for . */
+ if (len == 0 || (len == 1 && name[0] == '.'))
+ {
+ *result = dir;
+ return 0;
+ }
+ /* ..and for .. */
+ if (len == 2 && name[0] == '.' && name[1] == '.')
+ {
+ struct smb_inode_info *parent = SMB_INOP(dir)->dir;
+
+ if (parent->state == SMB_INODE_CACHED)
+ {
+ parent->state = SMB_INODE_LOOKED_UP;
+ }
+ *result = iget(dir->i_sb, smb_info_ino(parent));
+ iput(dir);
+ if (*result == 0)
+ {
+ return -EACCES;
+ }
+ return 0;
+ }
+ result_info = smb_find_dir_inode(dir, name, len);
+
+ in_tree:
+ if (result_info != NULL)
+ {
+ if (result_info->state == SMB_INODE_CACHED)
+ {
+ result_info->state = SMB_INODE_LOOKED_UP;
+ }
+ *result = iget(dir->i_sb, smb_info_ino(result_info));
+ iput(dir);
+
+ if (new_inode_info != NULL)
+ {
+ smb_kfree_s(new_inode_info,
+ sizeof(struct smb_inode_info));
+ }
+ 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;
+
+ if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0))
+ {
+ int first = c_last_returned_index;
+ int i;
+
+ i = first;
+ do
+ {
+ if (compare_filename(SMB_SERVER(dir), name, len,
+ &(c_entry[i])) == 0)
+ {
+ finfo = c_entry[i];
+ found_in_cache = 1;
+ break;
+ }
+ i = (i + 1) % c_size;
+ }
+ while (i != first);
+ }
+ if (found_in_cache == 0)
+ {
+ DPRINTK("smb_lookup: not found in cache: %s\n", name);
+ if (len > SMB_MAXNAMELEN)
+ {
+ iput(dir);
+ return -ENAMETOOLONG;
+ }
+ error = smb_proc_getattr(dir, name, len, &finfo);
+ if (error < 0)
+ {
+ iput(dir);
+ return error;
+ }
+ finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
+ }
+ new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
+ GFP_KERNEL);
+
+ /* Here somebody else might have inserted the inode */
+
+ result_info = smb_find_dir_inode(dir, name, len);
+ if (result_info != NULL)
+ {
+ goto in_tree;
+ }
+ new_inode_info->finfo = finfo;
+
+ DPRINTK("attr: %x\n", finfo.attr);
+
+ if ((*result = smb_iget(dir, new_inode_info)) == NULL)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return -EACCES;
+ }
+ DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info);
+ iput(dir);
+ return 0;
+}
+
+static int
+smb_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result)
+{
+ int error;
+ struct smb_dirent entry;
+ struct smb_inode_info *new_inode_info;
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_create: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
+ GFP_KERNEL);
+ if (new_inode_info == NULL)
+ {
+ iput(dir);
+ return -ENOMEM;
+ }
+ error = smb_proc_create(dir, name, len, 0, CURRENT_TIME);
+ if (error < 0)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ smb_invalid_dir_cache(dir->i_ino);
+
+ if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
+
+ new_inode_info->finfo = entry;
+
+ if ((*result = smb_iget(dir, new_inode_info)) == NULL)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ iput(dir);
+ return 0;
+}
+
+static int
+smb_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ iput(dir);
+ return -EINVAL;
+ }
+ if ((error = smb_proc_mkdir(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_rmdir(struct inode *dir, const char *name, int len)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_rmdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (smb_find_dir_inode(dir, name, len) != NULL)
+ {
+ error = -EBUSY;
+ } else
+ {
+ if ((error = smb_proc_rmdir(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_unlink(struct inode *dir, const char *name, int len)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_unlink: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (smb_find_dir_inode(dir, name, len) != NULL)
+ {
+ error = -EBUSY;
+ } else
+ {
+ if ((error = smb_proc_unlink(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_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;
+
+ if (!old_dir || !S_ISDIR(old_dir->i_mode))
+ {
+ printk("smb_rename: old inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+ if (!new_dir || !S_ISDIR(new_dir->i_mode))
+ {
+ printk("smb_rename: new inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+ if ((smb_find_dir_inode(old_dir, old_name, old_len) != NULL)
+ || (smb_find_dir_inode(new_dir, new_name, new_len) != NULL))
+ {
+ res = -EBUSY;
+ goto finished;
+ }
+ res = smb_proc_mv(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+
+ if (res == -EEXIST)
+ {
+ int res1 = smb_proc_unlink(old_dir, new_name, new_len);
+
+ if (res1 == 0)
+ {
+ res = smb_proc_mv(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ }
+ }
+ if (res == 0)
+ {
+ smb_invalid_dir_cache(old_dir->i_ino);
+ smb_invalid_dir_cache(new_dir->i_ino);
+ }
+ finished:
+ iput(old_dir);
+ iput(new_dir);
+ return res;
+}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
new file mode 100644
index 000000000..525c78ab9
--- /dev/null
+++ b/fs/smbfs/file.c
@@ -0,0 +1,244 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#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/smb_fs.h>
+#include <linux/malloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static inline int
+min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static int
+smb_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int
+smb_make_open(struct inode *i, int right)
+{
+ struct smb_dirent *dirent;
+
+ if (i == NULL)
+ {
+ printk("smb_make_open: got NULL inode\n");
+ return -EINVAL;
+ }
+ dirent = &(SMB_INOP(i)->finfo);
+
+ DDPRINTK("smb_make_open: dirent->opened = %d\n", dirent->opened);
+
+ if ((dirent->opened) == 0)
+ {
+ /* tries max. rights */
+ int open_result = smb_proc_open(SMB_SERVER(i),
+ SMB_INOP(i)->dir,
+ dirent->name, dirent->len,
+ dirent);
+ if (open_result)
+ {
+ return open_result;
+ }
+ }
+ if (((right == O_RDONLY) && ((dirent->access == O_RDONLY)
+ || (dirent->access == O_RDWR)))
+ || ((right == O_WRONLY) && ((dirent->access == O_WRONLY)
+ || (dirent->access == O_RDWR)))
+ || ((right == O_RDWR) && (dirent->access == O_RDWR)))
+ return 0;
+
+ return -EACCES;
+}
+
+static long
+smb_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int result, bufsize, to_read, already_read;
+ off_t pos;
+ int errno;
+
+ DPRINTK("smb_file_read: enter %s\n", SMB_FINFO(inode)->name);
+
+ if (!inode)
+ {
+ DPRINTK("smb_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("smb_file_read: read from non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+ if ((errno = smb_make_open(inode, O_RDONLY)) != 0)
+ return errno;
+
+ pos = file->f_pos;
+
+ if (pos + count > inode->i_size)
+ {
+ count = inode->i_size - pos;
+ }
+ if (count <= 0)
+ {
+ return 0;
+ }
+ bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+ already_read = 0;
+
+ /* First read in as much as possible for each bufsize. */
+ while (already_read < count)
+ {
+ to_read = min(bufsize, count - already_read);
+ result = smb_proc_read(SMB_SERVER(inode), SMB_FINFO(inode),
+ pos, to_read, buf, 1);
+ if (result < 0)
+ {
+ return result;
+ }
+ pos += result;
+ buf += result;
+ already_read += result;
+
+ if (result < to_read)
+ {
+ break;
+ }
+ }
+
+ file->f_pos = pos;
+
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ DPRINTK("smb_file_read: exit %s\n", SMB_FINFO(inode)->name);
+
+ return already_read;
+}
+
+static long
+smb_file_write(struct inode *inode, struct file *file, const char *buf,
+ unsigned long count)
+{
+ int result, bufsize, to_write, already_written;
+ off_t pos;
+ int errno;
+
+ if (!inode)
+ {
+ DPRINTK("smb_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("smb_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+ DPRINTK("smb_file_write: enter %s\n", SMB_FINFO(inode)->name);
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+ if ((errno = smb_make_open(inode, O_RDWR)) != 0)
+ {
+ return errno;
+ }
+ pos = file->f_pos;
+
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+
+ bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+ already_written = 0;
+
+ DPRINTK("smb_write_file: blkmode = %d, blkmode & 2 = %d\n",
+ SMB_SERVER(inode)->blkmode,
+ SMB_SERVER(inode)->blkmode & 2);
+
+ while (already_written < count)
+ {
+ to_write = min(bufsize, count - already_written);
+ result = smb_proc_write(SMB_SERVER(inode), SMB_FINFO(inode),
+ pos, to_write, buf);
+
+ if (result < 0)
+ {
+ return result;
+ }
+ pos += result;
+ buf += result;
+ already_written += result;
+
+ if (result < 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;
+ }
+ DPRINTK("smb_file_write: exit %s\n", SMB_FINFO(inode)->name);
+
+ return already_written;
+}
+
+static struct file_operations smb_file_operations =
+{
+ NULL, /* lseek - default */
+ smb_file_read, /* read */
+ smb_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ smb_ioctl, /* ioctl */
+ smb_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ smb_fsync, /* fsync */
+};
+
+struct inode_operations smb_file_inode_operations =
+{
+ &smb_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, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL /* truncate */
+};
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
new file mode 100644
index 000000000..b39403358
--- /dev/null
+++ b/fs/smbfs/inode.c
@@ -0,0 +1,474 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.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>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern int close_fp(struct file *filp);
+
+static void smb_put_inode(struct inode *);
+static void smb_read_inode(struct inode *);
+static void smb_put_super(struct super_block *);
+static void smb_statfs(struct super_block *, struct statfs *, int bufsiz);
+
+static struct super_operations smb_sops =
+{
+ smb_read_inode, /* read inode */
+ smb_notify_change, /* notify change */
+ NULL, /* write inode */
+ smb_put_inode, /* put inode */
+ smb_put_super, /* put superblock */
+ NULL, /* write superblock */
+ smb_statfs, /* stat filesystem */
+ NULL
+};
+
+/* smb_read_inode: Called from iget, it only traverses the allocated
+ smb_inode_info's and initializes the inode from the data found
+ there. It does not allocate or deallocate anything. */
+
+static void
+smb_read_inode(struct inode *inode)
+{
+ /* Our task should be extremely simple here. We only have to
+ look up the information somebody else (smb_iget) put into
+ the inode tree. */
+ struct smb_server *server = SMB_SERVER(inode);
+ struct smb_inode_info *inode_info
+ = smb_find_inode(server, inode->i_ino);
+
+ if (inode_info == NULL)
+ {
+ /* Ok, now we're in trouble. The inode info is not
+ there. What should we do now??? */
+ printk("smb_read_inode: inode info not found\n");
+ return;
+ }
+ inode_info->state = SMB_INODE_VALID;
+
+ SMB_INOP(inode) = inode_info;
+ inode->i_mode = inode_info->finfo.f_mode;
+ inode->i_nlink = inode_info->finfo.f_nlink;
+ inode->i_uid = inode_info->finfo.f_uid;
+ inode->i_gid = inode_info->finfo.f_gid;
+ inode->i_rdev = inode_info->finfo.f_rdev;
+ inode->i_size = inode_info->finfo.f_size;
+ inode->i_mtime = inode_info->finfo.f_mtime;
+ inode->i_ctime = inode_info->finfo.f_ctime;
+ inode->i_atime = inode_info->finfo.f_atime;
+ inode->i_blksize = inode_info->finfo.f_blksize;
+ inode->i_blocks = inode_info->finfo.f_blocks;
+
+ if (S_ISREG(inode->i_mode))
+ {
+ inode->i_op = &smb_file_inode_operations;
+ } else if (S_ISDIR(inode->i_mode))
+ {
+ inode->i_op = &smb_dir_inode_operations;
+ } else
+ {
+ inode->i_op = NULL;
+ }
+}
+
+static void
+smb_put_inode(struct inode *inode)
+{
+ struct smb_dirent *finfo = SMB_FINFO(inode);
+ struct smb_server *server = SMB_SERVER(inode);
+ struct smb_inode_info *info = SMB_INOP(inode);
+
+ if (S_ISDIR(inode->i_mode))
+ {
+ smb_invalid_dir_cache(inode->i_ino);
+ }
+ if (finfo->opened != 0)
+ {
+ if (smb_proc_close(server, finfo->fileid, inode->i_mtime))
+ {
+ /* We can't do anything but complain. */
+ DPRINTK("smb_put_inode: could not close\n");
+ }
+ }
+ smb_free_inode_info(info);
+ clear_inode(inode);
+}
+
+static void
+smb_put_super(struct super_block *sb)
+{
+ struct smb_server *server = &(SMB_SBP(sb)->s_server);
+
+ smb_proc_disconnect(server);
+ smb_dont_catch_keepalive(server);
+ close_fp(server->sock_file);
+
+ lock_super(sb);
+
+ smb_free_all_inodes(server);
+
+ smb_vfree(server->packet);
+ server->packet = NULL;
+
+ sb->s_dev = 0;
+ smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+
+ unlock_super(sb);
+
+ MOD_DEC_USE_COUNT;
+}
+
+struct smb_mount_data_v4
+{
+ int version;
+ unsigned int fd;
+ uid_t mounted_uid;
+ struct sockaddr_in addr;
+
+ char server_name[17];
+ char client_name[17];
+ char service[64];
+ char root_path[64];
+
+ char username[64];
+ char password[64];
+
+ unsigned short max_xmit;
+
+ uid_t uid;
+ gid_t gid;
+ mode_t file_mode;
+ mode_t dir_mode;
+};
+
+static int
+smb_get_mount_data(struct smb_mount_data *target, void *source)
+{
+ struct smb_mount_data_v4 *v4 = (struct smb_mount_data_v4 *) source;
+ struct smb_mount_data *cur = (struct smb_mount_data *) source;
+
+ if (source == NULL)
+ {
+ return 1;
+ }
+ if (cur->version == SMB_MOUNT_VERSION)
+ {
+ memcpy(target, cur, sizeof(struct smb_mount_data));
+ return 0;
+ }
+ if (v4->version == 4)
+ {
+ target->version = 5;
+ target->fd = v4->fd;
+ target->mounted_uid = v4->mounted_uid;
+ target->addr = v4->addr;
+
+ memcpy(target->server_name, v4->server_name, 17);
+ memcpy(target->client_name, v4->client_name, 17);
+ memcpy(target->service, v4->service, 64);
+ memcpy(target->root_path, v4->root_path, 64);
+ memcpy(target->username, v4->username, 64);
+ memcpy(target->password, v4->password, 64);
+
+ target->max_xmit = v4->max_xmit;
+ target->uid = v4->uid;
+ target->gid = v4->gid;
+ target->file_mode = v4->file_mode;
+ target->dir_mode = v4->dir_mode;
+
+ memset(target->domain, 0, 64);
+ strcpy(target->domain, "?");
+ return 0;
+ }
+ return 1;
+}
+
+struct super_block *
+smb_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct smb_mount_data data;
+ struct smb_server *server;
+ struct smb_sb_info *smb_sb;
+ unsigned int fd;
+ struct file *filp;
+ kdev_t dev = sb->s_dev;
+ int error;
+
+ if (smb_get_mount_data(&data, raw_data) != 0)
+ {
+ printk("smb_read_super: wrong data argument\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ fd = data.fd;
+ if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+ {
+ printk("smb_read_super: invalid file descriptor\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ if (!S_ISSOCK(filp->f_inode->i_mode))
+ {
+ printk("smb_read_super: not a socket!\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ /* We must malloc our own super-block info */
+ smb_sb = (struct smb_sb_info *) smb_kmalloc(sizeof(struct smb_sb_info),
+ GFP_KERNEL);
+
+ if (smb_sb == NULL)
+ {
+ printk("smb_read_super: could not alloc smb_sb_info\n");
+ return NULL;
+ }
+ filp->f_count += 1;
+
+ lock_super(sb);
+
+ SMB_SBP(sb) = smb_sb;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = SMB_SUPER_MAGIC;
+ sb->s_dev = dev;
+ sb->s_op = &smb_sops;
+
+ server = &(SMB_SBP(sb)->s_server);
+ server->sock_file = filp;
+ server->lock = 0;
+ server->wait = NULL;
+ server->packet = NULL;
+ server->max_xmit = data.max_xmit;
+ if (server->max_xmit <= 0)
+ {
+ server->max_xmit = SMB_DEF_MAX_XMIT;
+ }
+ server->tid = 0;
+ server->pid = current->pid;
+ server->mid = current->pid + 20;
+
+ 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;
+
+ smb_init_root(server);
+
+ error = smb_proc_connect(server);
+
+ unlock_super(sb);
+
+ if (error < 0)
+ {
+ sb->s_dev = 0;
+ DPRINTK("smb_read_super: Failed connection, bailing out "
+ "(error = %d).\n", -error);
+ goto fail;
+ }
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ server->case_handling = CASE_DEFAULT;
+ } else
+ {
+ server->case_handling = CASE_LOWER;
+ }
+
+ if ((error = smb_proc_dskattr(sb, &(SMB_SBP(sb)->s_attr))) < 0)
+ {
+ sb->s_dev = 0;
+ printk("smb_read_super: could not get super block "
+ "attributes\n");
+ goto fail;
+ }
+ smb_init_root_dirent(server, &(server->root.finfo));
+
+ if (!(sb->s_mounted = iget(sb, smb_info_ino(&(server->root)))))
+ {
+ sb->s_dev = 0;
+ printk("smb_read_super: get root inode failed\n");
+ goto fail;
+ }
+ MOD_INC_USE_COUNT;
+ return sb;
+
+ fail:
+ if (server->packet != NULL)
+ {
+ smb_vfree(server->packet);
+ server->packet = NULL;
+ }
+ filp->f_count -= 1;
+ smb_dont_catch_keepalive(server);
+ smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+ return NULL;
+}
+
+static void
+smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ int error;
+ struct smb_dskattr attr;
+ struct statfs tmp;
+
+ error = smb_proc_dskattr(sb, &attr);
+
+ if (error)
+ {
+ printk("smb_statfs: dskattr error = %d\n", -error);
+ attr.total = attr.allocblocks = attr.blocksize =
+ attr.free = 0;
+ }
+ tmp.f_type = SMB_SUPER_MAGIC;
+ tmp.f_bsize = attr.blocksize * attr.allocblocks;
+ tmp.f_blocks = attr.total;
+ tmp.f_bfree = attr.free;
+ tmp.f_bavail = attr.free;
+ tmp.f_files = -1;
+ tmp.f_ffree = -1;
+ tmp.f_namelen = SMB_MAXPATHLEN;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+int
+smb_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int error = 0;
+
+ if ((error = inode_change_ok(inode, attr)) < 0)
+ return error;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != SMB_SERVER(inode)->m.uid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_uid != SMB_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;
+
+ if ((attr->ia_valid & ATTR_SIZE) != 0)
+ {
+
+ if ((error = smb_make_open(inode, O_WRONLY)) < 0)
+ goto fail;
+
+ if ((error = smb_proc_trunc(SMB_SERVER(inode),
+ SMB_FINFO(inode)->fileid,
+ attr->ia_size)) < 0)
+ goto fail;
+
+ }
+ if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
+ {
+
+ struct smb_dirent finfo;
+
+ finfo.attr = 0;
+ finfo.f_size = inode->i_size;
+ finfo.f_blksize = inode->i_blksize;
+
+ if ((attr->ia_valid & ATTR_CTIME) != 0)
+ finfo.f_ctime = attr->ia_ctime;
+ else
+ finfo.f_ctime = inode->i_ctime;
+
+ if ((attr->ia_valid & ATTR_MTIME) != 0)
+ finfo.f_mtime = attr->ia_mtime;
+ else
+ finfo.f_mtime = inode->i_mtime;
+
+ if ((attr->ia_valid & ATTR_ATIME) != 0)
+ finfo.f_atime = attr->ia_atime;
+ else
+ finfo.f_atime = inode->i_atime;
+
+ if ((error = smb_proc_setattr(SMB_SERVER(inode),
+ inode, &finfo)) >= 0)
+ {
+ inode->i_ctime = finfo.f_ctime;
+ inode->i_mtime = finfo.f_mtime;
+ inode->i_atime = finfo.f_atime;
+ }
+ }
+ fail:
+ smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));
+
+ return error;
+}
+
+
+#ifdef DEBUG_SMB_MALLOC
+int smb_malloced;
+int smb_current_kmalloced;
+int smb_current_vmalloced;
+#endif
+
+static struct file_system_type smb_fs_type =
+{
+ smb_read_super, "smbfs", 0, NULL
+};
+
+int
+init_smb_fs(void)
+{
+ return register_filesystem(&smb_fs_type);
+}
+
+#ifdef MODULE
+int
+init_module(void)
+{
+ int status;
+
+ DPRINTK("smbfs: init_module called\n");
+
+#ifdef DEBUG_SMB_MALLOC
+ smb_malloced = 0;
+ smb_current_kmalloced = 0;
+ smb_current_vmalloced = 0;
+#endif
+
+ smb_init_dir_cache();
+
+ if ((status = init_smb_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void
+cleanup_module(void)
+{
+ DPRINTK("smbfs: cleanup_module called\n");
+ smb_free_dir_cache();
+ unregister_filesystem(&smb_fs_type);
+#ifdef DEBUG_SMB_MALLOC
+ printk("smb_malloced: %d\n", smb_malloced);
+ printk("smb_current_kmalloced: %d\n", smb_current_kmalloced);
+ printk("smb_current_vmalloced: %d\n", smb_current_vmalloced);
+#endif
+}
+
+#endif
diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c
new file mode 100644
index 000000000..d4685a60b
--- /dev/null
+++ b/fs/smbfs/ioctl.c
@@ -0,0 +1,29 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/smb_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+int
+smb_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ case SMB_IOC_GETMOUNTUID:
+ return put_user(SMB_SERVER(inode)->m.mounted_uid, (uid_t *) arg);
+
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/fs/smbfs/mmap.c b/fs/smbfs/mmap.c
new file mode 100644
index 000000000..9fd157b2a
--- /dev/null
+++ b/fs/smbfs/mmap.c
@@ -0,0 +1,126 @@
+/*
+ * mmap.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and 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/smb_fs.h>
+#include <linux/fcntl.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long
+smb_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 n;
+ int i;
+ int pos;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return 0;
+ 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 */
+ n = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 3 - 10;
+
+ if (smb_make_open(inode, O_RDONLY) < 0)
+ {
+ clear = PAGE_SIZE;
+ } else
+ {
+
+ for (i = 0; i < (PAGE_SIZE - clear); i += n)
+ {
+ int hunk, result;
+
+ hunk = PAGE_SIZE - i;
+ if (hunk > n)
+ hunk = n;
+
+ DDPRINTK("smb_file_mmap_nopage: reading\n");
+ DDPRINTK("smb_file_mmap_nopage: pos = %d\n", pos);
+ result = smb_proc_read(SMB_SERVER(inode),
+ SMB_FINFO(inode), pos, hunk,
+ (char *) (page + i), 0);
+ DDPRINTK("smb_file_mmap_nopage: result= %d\n", result);
+ if (result < 0)
+ break;
+ pos += result;
+ if (result < n)
+ {
+ i += result;
+ break;
+ }
+ }
+ }
+
+ tmp = page + PAGE_SIZE;
+ while (clear--)
+ {
+ *(char *) --tmp = 0;
+ }
+ return page;
+}
+
+struct vm_operations_struct smb_file_mmap =
+{
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ smb_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+
+/* This is used for a general mmap of a smb file */
+int
+smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
+{
+ DPRINTK("smb_mmap: called\n");
+
+ /* 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 = &smb_file_mmap;
+ return 0;
+}
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
new file mode 100644
index 000000000..ee998a89f
--- /dev/null
+++ b/fs/smbfs/proc.c
@@ -0,0 +1,1969 @@
+/*
+ * proc.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_fs.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>
+
+#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
+#define SMB_CMD(packet) (BVAL(packet,8))
+#define SMB_WCT(packet) (BVAL(packet, SMB_HEADER_LEN - 1))
+#define SMB_BCC(packet) smb_bcc(packet)
+#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
+
+#define SMB_DIRINFO_SIZE 43
+#define SMB_STATUS_SIZE 21
+
+static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
+
+static inline int
+min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static void
+str_upper(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ *name -= ('a' - 'A');
+ name++;
+ }
+}
+
+static void
+str_lower(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'A' && *name <= 'Z')
+ *name += ('a' - 'A');
+ name++;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* Encoding/Decoding section */
+/* */
+/*****************************************************************************/
+
+static inline byte *
+smb_decode_word(byte * p, word * data)
+{
+ *data = WVAL(p, 0);
+ return p + 2;
+}
+
+byte *
+smb_encode_smb_length(byte * p, dword len)
+{
+ BSET(p, 0, 0);
+ BSET(p, 1, 0);
+ BSET(p, 2, (len & 0xFF00) >> 8);
+ BSET(p, 3, (len & 0xFF));
+ if (len > 0xFFFF)
+ {
+ BSET(p, 1, 1);
+ }
+ return p + 4;
+}
+
+static byte *
+smb_encode_ascii(byte * p, const byte * name, int len)
+{
+ *p++ = 4;
+ strcpy(p, name);
+ return p + len + 1;
+}
+
+static byte *
+smb_encode_this_name(byte * p, const char *name, const int len)
+{
+ *p++ = '\\';
+ strncpy(p, name, len);
+ return p + len;
+}
+
+/* I put smb_encode_parents into a separate function so that the
+ recursion only takes 16 bytes on the stack per path component on a
+ 386. */
+
+static byte *
+smb_encode_parents(byte * p, struct smb_inode_info *ino)
+{
+ byte *q;
+
+ if (ino->dir == NULL)
+ {
+ return p;
+ }
+ q = smb_encode_parents(p, ino->dir);
+ if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN)
+ {
+ return p;
+ }
+ return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);
+}
+
+static byte *
+smb_encode_path(struct smb_server *server,
+ byte * p, struct smb_inode_info *dir,
+ const char *name, const int len)
+{
+ byte *start = p;
+ p = smb_encode_parents(p, dir);
+ p = smb_encode_this_name(p, name, len);
+ *p++ = 0;
+ if (server->protocol <= PROTOCOL_COREPLUS)
+ {
+ str_upper(start);
+ }
+ return p;
+}
+
+static byte *
+smb_decode_data(byte * p, byte * data, word * data_len, int fs)
+{
+ word len;
+
+ if (!(*p == 1 || *p == 5))
+ {
+ printk("smb_decode_data: Warning! Data block not starting "
+ "with 1 or 5\n");
+ }
+ len = WVAL(p, 1);
+ p += 3;
+
+ if (fs)
+ copy_to_user(data, p, len);
+ else
+ memcpy(data, p, len);
+
+ *data_len = len;
+
+ return p + len;
+}
+
+static byte *
+smb_name_mangle(byte * p, const byte * name)
+{
+ int len, pad = 0;
+
+ len = strlen(name);
+
+ if (len < 16)
+ pad = 16 - len;
+
+ *p++ = 2 * (len + pad);
+
+ while (*name)
+ {
+ *p++ = (*name >> 4) + 'A';
+ *p++ = (*name & 0x0F) + 'A';
+ name++;
+ }
+ while (pad--)
+ {
+ *p++ = 'C';
+ *p++ = 'A';
+ }
+ *p++ = '\0';
+
+ return p;
+}
+
+/* The following 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;
+}
+
+static int
+local2utc(int time)
+{
+ return time + sys_tz.tz_minuteswest * 60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+static int
+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. */
+
+static void
+date_unix2dos(int unix_date, byte * date, byte * time)
+{
+ int day, year, nl_day, month;
+
+ unix_date = utc2local(unix_date);
+ WSET(time, 0,
+ (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;
+ }
+ WSET(date, 0,
+ nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
+}
+
+
+
+/*****************************************************************************/
+/* */
+/* Support section. */
+/* */
+/*****************************************************************************/
+
+dword
+smb_len(byte * p)
+{
+ return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));
+}
+
+static word
+smb_bcc(byte * packet)
+{
+ int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
+ return WVAL(packet, pos);
+}
+
+/* smb_valid_packet: We check if packet fulfills the basic
+ requirements of a smb packet */
+
+static int
+smb_valid_packet(byte * packet)
+{
+ DDPRINTK("len: %d, wct: %d, bcc: %d\n",
+ smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
+ return (packet[4] == 0xff
+ && packet[5] == 'S'
+ && packet[6] == 'M'
+ && packet[7] == 'B'
+ && (smb_len(packet) + 4 == SMB_HEADER_LEN
+ + SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+}
+
+/* smb_verify: We check if we got the answer we expected, and if we
+ got enough data. If bcc == -1, we don't care. */
+
+static int
+smb_verify(byte * packet, int command, int wct, int bcc)
+{
+ return (SMB_CMD(packet) == command &&
+ SMB_WCT(packet) >= wct &&
+ (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
+}
+
+static int
+smb_errno(int errcls, int error)
+{
+ if (errcls == ERRDOS)
+ switch (error)
+ {
+ case ERRbadfunc:
+ return EINVAL;
+ case ERRbadfile:
+ return ENOENT;
+ case ERRbadpath:
+ return ENOENT;
+ case ERRnofids:
+ return EMFILE;
+ case ERRnoaccess:
+ return EACCES;
+ case ERRbadfid:
+ return EBADF;
+ case ERRbadmcb:
+ return EREMOTEIO;
+ case ERRnomem:
+ return ENOMEM;
+ case ERRbadmem:
+ return EFAULT;
+ case ERRbadenv:
+ return EREMOTEIO;
+ case ERRbadformat:
+ return EREMOTEIO;
+ case ERRbadaccess:
+ return EACCES;
+ case ERRbaddata:
+ return E2BIG;
+ case ERRbaddrive:
+ return ENXIO;
+ case ERRremcd:
+ return EREMOTEIO;
+ case ERRdiffdevice:
+ return EXDEV;
+ case ERRnofiles:
+ return 0;
+ case ERRbadshare:
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ case ERRfilexists:
+ return EEXIST;
+ case 87:
+ return 0; /* Unknown error!! */
+ /* This next error seems to occur on an mv when
+ * the destination exists */
+ case 183:
+ return EEXIST;
+ default:
+ return EIO;
+ } else if (errcls == ERRSRV)
+ switch (error)
+ {
+ case ERRerror:
+ return ENFILE;
+ case ERRbadpw:
+ return EINVAL;
+ case ERRbadtype:
+ return EIO;
+ case ERRaccess:
+ return EACCES;
+ default:
+ return EIO;
+ } else if (errcls == ERRHRD)
+ switch (error)
+ {
+ case ERRnowrite:
+ return EROFS;
+ case ERRbadunit:
+ return ENODEV;
+ case ERRnotready:
+ return EUCLEAN;
+ case ERRbadcmd:
+ return EIO;
+ case ERRdata:
+ return EIO;
+ case ERRbadreq:
+ return ERANGE;
+ case ERRbadshare:
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ default:
+ return EIO;
+ } else if (errcls == ERRCMD)
+ return EIO;
+ return 0;
+}
+
+static void
+smb_lock_server(struct smb_server *server)
+{
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+}
+
+static void
+smb_unlock_server(struct smb_server *server)
+{
+ if (server->lock != 1)
+ {
+ printk("smb_unlock_server: was not locked!\n");
+ }
+ server->lock = 0;
+ wake_up(&server->wait);
+}
+
+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_server *s, int command, int wct, int bcc)
+{
+ int result = 0;
+ s->rcls = 0;
+ s->err = 0;
+
+ if (smb_request(s) < 0)
+ {
+ DPRINTK("smb_request failed\n");
+ result = -EIO;
+ } else if (smb_valid_packet(s->packet) != 0)
+ {
+ DPRINTK("not a valid packet!\n");
+ result = -EIO;
+ } else if (s->rcls != 0)
+ {
+ result = -smb_errno(s->rcls, s->err);
+ } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+ {
+ DPRINTK("smb_verify failed\n");
+ result = -EIO;
+ }
+ return result;
+}
+
+/* smb_retry: This function should be called when smb_request_ok has
+ indicated an error. If the error was indicated because the
+ connection was killed, we try to reconnect. If smb_retry returns 0,
+ the error was indicated for another reason, so a retry would not be
+ of any use. */
+
+static int
+smb_retry(struct smb_server *server)
+{
+ if (server->state != CONN_INVALID)
+ {
+ return 0;
+ }
+ if (smb_release(server) < 0)
+ {
+ DPRINTK("smb_retry: smb_release failed\n");
+ server->state = CONN_RETRIED;
+ return 0;
+ }
+ if (smb_proc_reconnect(server) < 0)
+ {
+ DPRINTK("smb_proc_reconnect failed\n");
+ server->state = CONN_RETRIED;
+ return 0;
+ }
+ server->state = CONN_VALID;
+ return 1;
+}
+
+static int
+smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
+{
+ int result = smb_request_ok(s, command, wct, bcc);
+
+ smb_unlock_server(s);
+
+ return result;
+}
+
+/* smb_setup_header: We completely set up the packet. You only have to
+ insert the command-specific fields */
+
+__u8 *
+smb_setup_header(struct smb_server * server, byte command, word wct, word bcc)
+{
+ dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
+ byte *p = server->packet;
+ byte *buf = server->packet;
+
+ p = smb_encode_smb_length(p, xmit_len - 4);
+
+ BSET(p, 0, 0xff);
+ BSET(p, 1, 'S');
+ BSET(p, 2, 'M');
+ BSET(p, 3, 'B');
+ BSET(p, 4, command);
+
+ p += 5;
+ memset(p, '\0', 19);
+ p += 19;
+ p += 8;
+
+ WSET(buf, smb_tid, server->tid);
+ WSET(buf, smb_pid, server->pid);
+ WSET(buf, smb_uid, server->server_uid);
+ WSET(buf, smb_mid, server->mid);
+
+ if (server->protocol > PROTOCOL_CORE)
+ {
+ BSET(buf, smb_flg, 0x8);
+ WSET(buf, smb_flg2, 0x3);
+ }
+ *p++ = wct; /* wct */
+ p += 2 * wct;
+ WSET(p, 0, bcc);
+ return p + 2;
+}
+
+/* smb_setup_header_exclusive waits on server->lock and locks the
+ server, when it's free. You have to unlock it manually when you're
+ finished with server->packet! */
+
+static byte *
+smb_setup_header_exclusive(struct smb_server *server,
+ byte command, word wct, word bcc)
+{
+ smb_lock_server(server);
+ return smb_setup_header(server, command, wct, bcc);
+}
+
+static void
+smb_setup_bcc(struct smb_server *server, byte * p)
+{
+ __u8 *packet = server->packet;
+ __u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
+ __u16 bcc = p - (pbcc + 2);
+
+ WSET(pbcc, 0, bcc);
+ smb_encode_smb_length(packet,
+ SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
+}
+
+
+/*****************************************************************************/
+/* */
+/* File operation section. */
+/* */
+/*****************************************************************************/
+
+int
+smb_proc_open(struct smb_server *server,
+ struct smb_inode_info *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ int error;
+ char *p;
+ char *buf;
+ const word o_attr = aSYSTEM | aHIDDEN | aDIR;
+
+ DPRINTK("smb_proc_open: name=%s\n", name);
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ if (entry->opened != 0)
+ {
+ /* Somebody else opened the file while we slept */
+ smb_unlock_server(server);
+ return 0;
+ }
+ retry:
+ p = smb_setup_header(server, SMBopen, 2, 0);
+ WSET(buf, smb_vwv0, 0x42); /* read/write */
+ WSET(buf, smb_vwv1, o_attr);
+ *p++ = 4;
+ p = smb_encode_path(server, p, dir, name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ {
+
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ if ((error != -EACCES) && (error != -ETXTBSY)
+ && (error != -EROFS))
+ {
+ smb_unlock_server(server);
+ return error;
+ }
+ p = smb_setup_header(server, SMBopen, 2, 0);
+ WSET(buf, smb_vwv0, 0x40); /* read only */
+ WSET(buf, smb_vwv1, o_attr);
+ *p++ = 4;
+ p = smb_encode_path(server, p, dir, name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ }
+ /* We should now have data in vwv[0..6]. */
+
+ entry->fileid = WVAL(buf, smb_vwv0);
+ entry->attr = WVAL(buf, smb_vwv1);
+ entry->f_ctime = entry->f_atime =
+ entry->f_mtime = local2utc(DVAL(buf, smb_vwv2));
+ entry->f_size = DVAL(buf, smb_vwv4);
+ entry->access = WVAL(buf, smb_vwv6);
+
+ entry->opened = 1;
+ entry->access &= 3;
+
+ smb_unlock_server(server);
+
+ DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
+ return 0;
+}
+
+int
+smb_proc_close(struct smb_server *server,
+ __u16 fileid, __u32 mtime)
+{
+ char *buf;
+
+ smb_setup_header_exclusive(server, SMBclose, 3, 0);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, fileid);
+ DSET(buf, smb_vwv1, utc2local(mtime));
+
+ return smb_request_ok_unlock(server, SMBclose, 0, 0);
+}
+
+/* In smb_proc_read and smb_proc_write we do not retry, because the
+ file-id would not be valid after a reconnection. */
+
+/* smb_proc_read: fs indicates if it should be copied with
+ copy_to_user. */
+
+int
+smb_proc_read(struct smb_server *server, struct smb_dirent *finfo,
+ off_t offset, long count, char *data, int fs)
+{
+ word returned_count, data_len;
+ char *buf;
+ int error;
+
+ smb_setup_header_exclusive(server, SMBread, 5, 0);
+ buf = server->packet;
+
+ WSET(buf, smb_vwv0, finfo->fileid);
+ WSET(buf, smb_vwv1, count);
+ DSET(buf, smb_vwv2, offset);
+ WSET(buf, smb_vwv4, 0);
+
+ if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
+ {
+ smb_unlock_server(server);
+ return error;
+ }
+ returned_count = WVAL(buf, smb_vwv0);
+
+ smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
+
+ smb_unlock_server(server);
+
+ if (returned_count != data_len)
+ {
+ printk("smb_proc_read: Warning, returned_count != data_len\n");
+ printk("smb_proc_read: ret_c=%d, data_len=%d\n",
+ returned_count, data_len);
+ }
+ return data_len;
+}
+
+int
+smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
+ off_t offset, int count, const char *data)
+{
+ int res = 0;
+ char *buf;
+ byte *p;
+
+ p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, finfo->fileid);
+ WSET(buf, smb_vwv1, count);
+ DSET(buf, smb_vwv2, offset);
+ WSET(buf, smb_vwv4, 0);
+
+ *p++ = 1;
+ WSET(p, 0, count);
+ copy_from_user(p + 2, data, count);
+
+ if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+ {
+ res = WVAL(buf, smb_vwv0);
+ }
+ smb_unlock_server(server);
+
+ return res;
+}
+
+int
+smb_proc_create(struct inode *dir, const char *name, int len,
+ word attr, time_t ctime)
+{
+ int error;
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+ __u16 fileid;
+
+ smb_lock_server(server);
+ buf = server->packet;
+ retry:
+ p = smb_setup_header(server, SMBcreate, 3, 0);
+ WSET(buf, smb_vwv0, attr);
+ DSET(buf, smb_vwv1, utc2local(ctime));
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ fileid = WVAL(buf, smb_vwv0);
+ smb_unlock_server(server);
+
+ smb_proc_close(server, fileid, CURRENT_TIME);
+
+ return 0;
+}
+
+int
+smb_proc_mv(struct inode *odir, const char *oname, const int olen,
+ struct inode *ndir, const char *nname, const int nlen)
+{
+ char *p;
+ struct smb_server *server = SMB_SERVER(odir);
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBmv, 1, 0);
+ WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_mkdir(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ int result;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, SMBmkdir, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_rmdir(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ int result;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ smb_lock_server(server);
+
+
+ retry:
+ p = smb_setup_header(server, SMBrmdir, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_unlink(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBunlink, 1, 0);
+ WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_trunc(struct smb_server *server, word fid, dword length)
+{
+ char *p;
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBwrite, 5, 0);
+ WSET(buf, smb_vwv0, fid);
+ WSET(buf, smb_vwv1, 0);
+ DSET(buf, smb_vwv2, length);
+ WSET(buf, smb_vwv4, 0);
+ p = smb_encode_ascii(p, "", 0);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+static void
+smb_init_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ memset(entry, 0, sizeof(struct smb_dirent));
+
+ entry->f_nlink = 1;
+ entry->f_uid = server->m.uid;
+ entry->f_gid = server->m.gid;
+ entry->f_blksize = 512;
+}
+
+static void
+smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ if ((entry->attr & aDIR) != 0)
+ {
+ entry->f_mode = server->m.dir_mode;
+ entry->f_size = 512;
+ } else
+ {
+ entry->f_mode = server->m.file_mode;
+ }
+
+ if ((entry->f_blksize != 0) && (entry->f_size != 0))
+ {
+ entry->f_blocks =
+ (entry->f_size - 1) / entry->f_blksize + 1;
+ } else
+ {
+ entry->f_blocks = 0;
+ }
+ return;
+}
+
+void
+smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ smb_init_dirent(server, entry);
+ entry->attr = aDIR;
+ entry->f_ino = 1;
+ smb_finish_dirent(server, entry);
+}
+
+
+static char *
+smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry)
+{
+ smb_init_dirent(server, entry);
+
+ p += SMB_STATUS_SIZE; /* reserved (search_status) */
+ entry->attr = BVAL(p, 0);
+ entry->f_mtime = entry->f_atime = entry->f_ctime =
+ date_dos2unix(WVAL(p, 1), WVAL(p, 3));
+ entry->f_size = DVAL(p, 5);
+ entry->len = strlen(p + 9);
+ if (entry->len > 12)
+ {
+ entry->len = 12;
+ }
+ memcpy(entry->name, p + 9, entry->len);
+ entry->name[entry->len] = '\0';
+ while (entry->len > 2)
+ {
+ /* Pathworks fills names with spaces */
+ entry->len -= 1;
+ if (entry->name[entry->len] == ' ')
+ {
+ entry->name[entry->len] = '\0';
+ }
+ }
+ switch (server->case_handling)
+ {
+ case CASE_UPPER:
+ str_upper(entry->name);
+ break;
+ case CASE_LOWER:
+ str_lower(entry->name);
+ break;
+ default:
+ break;
+ }
+ DPRINTK("smb_decode_dirent: name = %s\n", entry->name);
+ smb_finish_dirent(server, entry);
+ return p + 22;
+}
+
+/* This routine is used to read in directory entries from the network.
+ Note that it is for short directory name seeks, i.e.: protocol <
+ PROTOCOL_LANMAN2 */
+
+static int
+smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *entry)
+{
+ char *p;
+ char *buf;
+ int error;
+ int result;
+ int i;
+ int first, total_count;
+ struct smb_dirent *current_entry;
+ word bcc;
+ word count;
+ char status[SMB_STATUS_SIZE];
+ int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
+
+ DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos);
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ first = 1;
+ total_count = 0;
+ current_entry = entry;
+
+ while (1)
+ {
+ if (first == 1)
+ {
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(buf, smb_vwv0, entries_asked);
+ WSET(buf, smb_vwv1, aDIR);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3);
+ *p++ = 5;
+ WSET(p, 0, 0);
+ p += 2;
+ } else
+ {
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(buf, smb_vwv0, entries_asked);
+ WSET(buf, smb_vwv1, aDIR);
+ p = smb_encode_ascii(p, "", 0);
+ *p++ = 5;
+ WSET(p, 0, SMB_STATUS_SIZE);
+ p += 2;
+ memcpy(p, status, SMB_STATUS_SIZE);
+ p += SMB_STATUS_SIZE;
+ }
+
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
+ {
+ if ((server->rcls == ERRDOS)
+ && (server->err == ERRnofiles))
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ } else
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ result = error;
+ goto unlock_return;
+ }
+ }
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, &count);
+ p = smb_decode_word(p, &bcc);
+
+ first = 0;
+
+ if (count <= 0)
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ }
+ if (bcc != count * SMB_DIRINFO_SIZE + 3)
+ {
+ result = -EIO;
+ goto unlock_return;
+ }
+ p += 3; /* Skipping VBLOCK header
+ (5, length lo, length hi). */
+
+ /* Read the last entry into the status field. */
+ memcpy(status,
+ SMB_BUF(server->packet) + 3 +
+ (count - 1) * SMB_DIRINFO_SIZE,
+ SMB_STATUS_SIZE);
+
+ /* Now we are ready to parse smb directory entries. */
+
+ for (i = 0; i < count; i++)
+ {
+ if (total_count < fpos)
+ {
+ p += SMB_DIRINFO_SIZE;
+ DDPRINTK("smb_proc_readdir: skipped entry.\n");
+ DDPRINTK(" total_count = %d\n"
+ " i = %d, fpos = %d\n",
+ total_count, i, fpos);
+ } else if (total_count >= fpos + cache_size)
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ } else
+ {
+ p = smb_decode_dirent(server, p,
+ current_entry);
+ current_entry->f_pos = total_count;
+ DDPRINTK("smb_proc_readdir: entry->f_pos = "
+ "%lu\n", entry->f_pos);
+ current_entry += 1;
+ }
+ total_count += 1;
+ }
+ }
+ unlock_return:
+ smb_unlock_server(server);
+ return result;
+}
+
+/* interpret a long filename structure - this is mostly guesses at the
+ moment. The length of the structure is returned. The structure of
+ a long filename depends on the info level. 260 is used by NT and 2
+ is used by OS/2. */
+
+static char *
+smb_decode_long_dirent(struct smb_server *server, char *p,
+ struct smb_dirent *entry, int level)
+{
+ char *result;
+
+ smb_init_dirent(server, entry);
+
+ switch (level)
+ {
+ /* We might add more levels later... */
+ case 1:
+ entry->len = BVAL(p, 26);
+ strncpy(entry->name, p + 27, entry->len);
+ entry->name[entry->len] = '\0';
+ entry->f_size = DVAL(p, 16);
+ entry->attr = BVAL(p, 24);
+
+ entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
+ entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
+ entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
+ result = p + 28 + BVAL(p, 26);
+ break;
+
+ default:
+ DPRINTK("Unknown long filename format %d\n", level);
+ result = p + WVAL(p, 0);
+ }
+
+ switch (server->case_handling)
+ {
+ case CASE_UPPER:
+ str_upper(entry->name);
+ break;
+ case CASE_LOWER:
+ str_lower(entry->name);
+ break;
+ default:
+ break;
+ }
+
+ smb_finish_dirent(server, entry);
+ return result;
+}
+
+int
+smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *cache)
+{
+ /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ const int info_level = 1;
+ const int max_matches = 512;
+
+ char *p;
+ char *lastname;
+ int lastname_len;
+ int i;
+ int first, entries, entries_seen;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ __u16 command;
+
+ int result;
+
+ int ff_resume_key = 0;
+ int ff_searchcount = 0;
+ int ff_eos = 0;
+ int ff_lastname = 0;
+ int ff_dir_handle = 0;
+ int loop_count = 0;
+
+ char param[SMB_MAXPATHLEN + 2 + 12];
+ int mask_len;
+ unsigned char *mask = &(param[12]);
+
+ mask_len = smb_encode_path(server, mask,
+ SMB_INOP(dir), "*", 1) - mask;
+
+ mask[mask_len] = 0;
+ mask[mask_len + 1] = 0;
+
+ DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
+ cache_size, fpos, mask);
+
+ smb_lock_server(server);
+
+ retry:
+
+ first = 1;
+ entries = 0;
+ entries_seen = 2;
+
+ while (ff_eos == 0)
+ {
+ loop_count += 1;
+ if (loop_count > 200)
+ {
+ printk("smb_proc_readdir_long: "
+ "Looping in FIND_NEXT??\n");
+ break;
+ }
+ if (first != 0)
+ {
+ command = TRANSACT2_FINDFIRST;
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, 8 + 4 + 2); /* resume required +
+ close on end +
+ continue */
+ WSET(param, 6, info_level);
+ DSET(param, 8, 0);
+ } else
+ {
+ command = TRANSACT2_FINDNEXT;
+ DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+ ff_dir_handle, ff_resume_key, ff_lastname, mask);
+ WSET(param, 0, ff_dir_handle);
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, info_level);
+ DSET(param, 6, ff_resume_key); /* ff_resume_key */
+ WSET(param, 10, 8 + 4 + 2); /* resume required +
+ close on end +
+ continue */
+#ifdef CONFIG_SMB_WIN95
+ /* Windows 95 is not able to deliver answers
+ to FIND_NEXT fast enough, so sleep 0.2 seconds */
+ current->timeout = jiffies + HZ / 5;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ current->timeout = 0;
+#endif
+ }
+
+ result = smb_trans2_request(server, command,
+ 0, NULL, 12 + mask_len + 2, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ DPRINTK("smb_proc_readdir_long: "
+ "got error from trans2_request\n");
+ break;
+ }
+ if (server->rcls != 0)
+ {
+ result = -EIO;
+ break;
+ }
+ /* parse out some important return info */
+ if (first != 0)
+ {
+ ff_dir_handle = WVAL(resp_param, 0);
+ ff_searchcount = WVAL(resp_param, 2);
+ ff_eos = WVAL(resp_param, 4);
+ ff_lastname = WVAL(resp_param, 8);
+ } else
+ {
+ ff_searchcount = WVAL(resp_param, 0);
+ ff_eos = WVAL(resp_param, 2);
+ ff_lastname = WVAL(resp_param, 6);
+ }
+
+ if (ff_searchcount == 0)
+ {
+ break;
+ }
+ /* point to the data bytes */
+ p = resp_data;
+
+ /* we might need the lastname for continuations */
+ lastname = "";
+ lastname_len = 0;
+ if (ff_lastname > 0)
+ {
+ switch (info_level)
+ {
+ case 260:
+ lastname = p + ff_lastname;
+ lastname_len = resp_data_len - ff_lastname;
+ ff_resume_key = 0;
+ break;
+ case 1:
+ lastname = p + ff_lastname + 1;
+ lastname_len = BVAL(p, ff_lastname);
+ ff_resume_key = 0;
+ break;
+ }
+ }
+ lastname_len = min(lastname_len, 256);
+ strncpy(mask, lastname, lastname_len);
+ mask[lastname_len] = '\0';
+
+ /* Now we are ready to parse smb directory entries. */
+
+ for (i = 0; i < ff_searchcount; i++)
+ {
+ struct smb_dirent *entry = &(cache[entries]);
+
+ p = smb_decode_long_dirent(server, p,
+ entry, info_level);
+
+ DDPRINTK("smb_readdir_long: got %s\n", entry->name);
+
+ if ((entry->name[0] == '.')
+ && ((entry->name[1] == '\0')
+ || ((entry->name[1] == '.')
+ && (entry->name[2] == '\0'))))
+ {
+ /* ignore . and .. from the server */
+ continue;
+ }
+ if (entries_seen >= fpos)
+ {
+ entry->f_pos = entries_seen;
+ entries += 1;
+ }
+ if (entries >= cache_size)
+ {
+ goto finished;
+ }
+ entries_seen += 1;
+ }
+
+ DPRINTK("received %d entries (eos=%d resume=%d)\n",
+ ff_searchcount, ff_eos, ff_resume_key);
+
+ first = 0;
+ }
+
+ finished:
+ smb_unlock_server(server);
+ return entries;
+}
+
+int
+smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *entry)
+{
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ return smb_proc_readdir_long(server, dir, fpos, cache_size,
+ entry);
+ else
+ return smb_proc_readdir_short(server, dir, fpos, cache_size,
+ entry);
+}
+
+static int
+smb_proc_getattr_core(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ int result;
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ DDPRINTK("smb_proc_getattr: %s\n", name);
+
+ retry:
+ p = smb_setup_header(server, SMBgetatr, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return result;
+ }
+ entry->attr = WVAL(buf, smb_vwv0);
+ entry->f_ctime = entry->f_atime =
+ entry->f_mtime = local2utc(DVAL(buf, smb_vwv1));
+
+ entry->f_size = DVAL(buf, smb_vwv3);
+ smb_unlock_server(server);
+ return 0;
+}
+
+static int
+smb_proc_getattr_trans2(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ struct smb_server *server = SMB_SERVER(dir);
+ char param[SMB_MAXPATHLEN + 20];
+ char *p;
+ int result;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
+ DSET(param, 2, 0);
+ p = smb_encode_path(server, param + 6, SMB_INOP(dir), name, len);
+
+ smb_lock_server(server);
+ retry:
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (server->rcls != 0)
+ {
+ smb_unlock_server(server);
+ return -smb_errno(server->rcls, server->err);
+ }
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return result;
+ }
+ if (resp_data_len < 22)
+ {
+ smb_unlock_server(server);
+ return -ENOENT;
+ }
+ entry->f_ctime = date_dos2unix(WVAL(resp_data, 2),
+ WVAL(resp_data, 0));
+ entry->f_atime = date_dos2unix(WVAL(resp_data, 6),
+ WVAL(resp_data, 4));
+ entry->f_mtime = date_dos2unix(WVAL(resp_data, 10),
+ WVAL(resp_data, 8));
+ entry->f_size = DVAL(resp_data, 12);
+ entry->attr = WVAL(resp_data, 20);
+ smb_unlock_server(server);
+
+ return 0;
+}
+
+int
+smb_proc_getattr(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ struct smb_server *server = SMB_SERVER(dir);
+ int result = 0;
+
+ smb_init_dirent(server, entry);
+
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ result = smb_proc_getattr_trans2(dir, name, len, entry);
+ }
+ if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0))
+ {
+ result = smb_proc_getattr_core(dir, name, len, entry);
+ }
+ smb_finish_dirent(server, entry);
+
+ entry->len = len;
+ memcpy(entry->name, name, len);
+ /* entry->name is null terminated from smb_init_dirent */
+
+ return result;
+}
+
+
+/* In core protocol, there is only 1 time to be set, we use
+ entry->f_mtime, to make touch work. */
+static int
+smb_proc_setattr_core(struct smb_server *server,
+ struct inode *i, struct smb_dirent *new_finfo)
+{
+ char *p;
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBsetatr, 8, 0);
+ WSET(buf, smb_vwv0, new_finfo->attr);
+ DSET(buf, smb_vwv1, utc2local(new_finfo->f_mtime));
+ *p++ = 4;
+ p = smb_encode_path(server, p,
+ SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
+ SMB_INOP(i)->finfo.len);
+ p = smb_encode_ascii(p, "", 0);
+
+ smb_setup_bcc(server, p);
+ if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+static int
+smb_proc_setattr_trans2(struct smb_server *server,
+ struct inode *i, struct smb_dirent *new_finfo)
+{
+ char param[SMB_MAXPATHLEN + 20];
+ char data[26];
+ char *p;
+ int result;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
+ DSET(param, 2, 0);
+ p = smb_encode_path(server, param + 6,
+ SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
+ SMB_INOP(i)->finfo.len);
+
+ date_unix2dos(new_finfo->f_ctime, &(data[0]), &(data[2]));
+ date_unix2dos(new_finfo->f_atime, &(data[4]), &(data[6]));
+ date_unix2dos(new_finfo->f_mtime, &(data[8]), &(data[10]));
+ DSET(data, 12, new_finfo->f_size);
+ DSET(data, 16, new_finfo->f_blksize);
+ WSET(data, 20, new_finfo->attr);
+ WSET(data, 22, 0);
+
+ smb_lock_server(server);
+ retry:
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ 26, data, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (server->rcls != 0)
+ {
+ smb_unlock_server(server);
+ return -smb_errno(server->rcls, server->err);
+ }
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return 0;
+}
+
+int
+smb_proc_setattr(struct smb_server *server, struct inode *inode,
+ struct smb_dirent *new_finfo)
+{
+ int result;
+
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ result = smb_proc_setattr_trans2(server, inode, new_finfo);
+ }
+ if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0))
+ {
+ result = smb_proc_setattr_core(server, inode, new_finfo);
+ }
+ return result;
+}
+
+int
+smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
+{
+ int error;
+ char *p;
+ struct smb_server *server = &(SMB_SBP(super)->s_server);
+
+ smb_lock_server(server);
+
+ retry:
+ smb_setup_header(server, SMBdskattr, 0, 0);
+
+ if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, &attr->total);
+ p = smb_decode_word(p, &attr->allocblocks);
+ p = smb_decode_word(p, &attr->blocksize);
+ p = smb_decode_word(p, &attr->free);
+ smb_unlock_server(server);
+ return 0;
+}
+
+/*****************************************************************************/
+/* */
+/* Mount/umount operations. */
+/* */
+/*****************************************************************************/
+
+struct smb_prots
+{
+ enum smb_protocol prot;
+ const char *name;
+};
+
+/* smb_proc_reconnect: We expect the server to be locked, so that you
+ can call the routine from within smb_retry. The socket must be
+ created, like after a user-level socket()-call. It may not be
+ connected. */
+
+int
+smb_proc_reconnect(struct smb_server *server)
+{
+ struct smb_prots prots[] =
+ {
+ {PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"},
+#ifdef LANMAN1
+ {PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1, "LANMAN1.0"},
+#endif
+#ifdef LANMAN2
+ {PROTOCOL_LANMAN2, "LM1.2X002"},
+#endif
+#ifdef NT1
+ {PROTOCOL_NT1, "NT LM 0.12"},
+ {PROTOCOL_NT1, "NT LANMAN 1.0"},
+#endif
+ {-1, NULL}};
+ char dev[] = "A:";
+ int i, plength;
+ int max_xmit = 1024; /* Space needed for first request. */
+ int given_max_xmit = server->m.max_xmit;
+ int result;
+ byte *p;
+
+ if ((result = smb_connect(server)) < 0)
+ {
+ DPRINTK("smb_proc_reconnect: could not smb_connect\n");
+ goto fail;
+ }
+ /* Here we assume that the connection is valid */
+ server->state = CONN_VALID;
+
+ if (server->packet != NULL)
+ {
+ smb_vfree(server->packet);
+ server->packet_size = 0;
+ }
+ server->packet = smb_vmalloc(max_xmit);
+
+ if (server->packet == NULL)
+ {
+ printk("smb_proc_connect: No memory! Bailing out.\n");
+ result = -ENOMEM;
+ goto fail;
+ }
+ server->packet_size = server->max_xmit = max_xmit;
+
+ /*
+ * Start with an RFC1002 session request packet.
+ */
+ p = server->packet + 4;
+
+ p = smb_name_mangle(p, server->m.server_name);
+ p = smb_name_mangle(p, server->m.client_name);
+
+ smb_encode_smb_length(server->packet,
+ (void *) p - (void *) (server->packet));
+
+ server->packet[0] = 0x81; /* SESSION REQUEST */
+
+ if (smb_catch_keepalive(server) < 0)
+ {
+ printk("smb_proc_connect: could not catch_keepalives\n");
+ }
+ if ((result = smb_request(server)) < 0)
+ {
+ DPRINTK("smb_proc_connect: Failed to send SESSION REQUEST.\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ if (server->packet[0] != 0x82)
+ {
+ printk("smb_proc_connect: Did not receive positive response "
+ "(err = %x)\n",
+ server->packet[0]);
+ smb_dont_catch_keepalive(server);
+ result = -EIO;
+ goto fail;
+ }
+ DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
+
+ /* Now we are ready to send a SMB Negotiate Protocol packet. */
+ memset(server->packet, 0, SMB_HEADER_LEN);
+
+ plength = 0;
+ for (i = 0; prots[i].name != NULL; i++)
+ {
+ plength += strlen(prots[i].name) + 2;
+ }
+
+ smb_setup_header(server, SMBnegprot, 0, plength);
+
+ p = SMB_BUF(server->packet);
+
+ for (i = 0; prots[i].name != NULL; i++)
+ {
+ *p++ = 2;
+ strcpy(p, prots[i].name);
+ p += strlen(prots[i].name) + 1;
+ }
+
+ if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0)
+ {
+ DPRINTK("smb_proc_connect: Failure requesting SMBnegprot\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ } else
+ {
+ DDPRINTK("smb_proc_connect: Request SMBnegprot..");
+ }
+
+ DDPRINTK("Verified!\n");
+
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, (word *) & i);
+ server->protocol = prots[i].prot;
+
+ DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
+ prots[i].name);
+
+ if (server->protocol >= PROTOCOL_LANMAN1)
+ {
+
+ word passlen = strlen(server->m.password);
+ word userlen = strlen(server->m.username);
+
+ DPRINTK("smb_proc_connect: password = %s\n",
+ server->m.password);
+ DPRINTK("smb_proc_connect: usernam = %s\n",
+ server->m.username);
+ DPRINTK("smb_proc_connect: blkmode = %d\n",
+ WVAL(server->packet, smb_vwv5));
+
+ if (server->protocol >= PROTOCOL_NT1)
+ {
+ server->max_xmit = DVAL(server->packet, smb_vwv3 + 1);
+ server->maxmux = WVAL(server->packet, smb_vwv1 + 1);
+ server->maxvcs = WVAL(server->packet, smb_vwv2 + 1);
+ server->blkmode = DVAL(server->packet, smb_vwv9 + 1);
+ server->sesskey = DVAL(server->packet, smb_vwv7 + 1);
+ } else
+ {
+ server->max_xmit = WVAL(server->packet, smb_vwv2);
+ server->maxmux = WVAL(server->packet, smb_vwv3);
+ server->maxvcs = WVAL(server->packet, smb_vwv4);
+ server->blkmode = WVAL(server->packet, smb_vwv5);
+ server->sesskey = DVAL(server->packet, smb_vwv6);
+ }
+
+ if (server->max_xmit < given_max_xmit)
+ {
+ /* We do not distinguish between the client
+ requests and the server response. */
+ given_max_xmit = server->max_xmit;
+ }
+ if (server->protocol >= PROTOCOL_NT1)
+ {
+ char *workgroup = server->m.domain;
+ char *OS_id = "Unix";
+ char *client_id = "ksmbfs";
+
+ smb_setup_header(server, SMBsesssetupX, 13,
+ 5 + userlen + passlen +
+ strlen(workgroup) + strlen(OS_id) +
+ strlen(client_id));
+
+ WSET(server->packet, smb_vwv0, 0x00ff);
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, given_max_xmit);
+ WSET(server->packet, smb_vwv3, 2);
+ WSET(server->packet, smb_vwv4, server->pid);
+ DSET(server->packet, smb_vwv5, server->sesskey);
+ WSET(server->packet, smb_vwv7, passlen + 1);
+ WSET(server->packet, smb_vwv8, 0);
+ WSET(server->packet, smb_vwv9, 0);
+
+ p = SMB_BUF(server->packet);
+ strcpy(p, server->m.password);
+ p += passlen + 1;
+ strcpy(p, server->m.username);
+ p += userlen + 1;
+ strcpy(p, workgroup);
+ p += strlen(p) + 1;
+ strcpy(p, OS_id);
+ p += strlen(p) + 1;
+ strcpy(p, client_id);
+ } else
+ {
+ smb_setup_header(server, SMBsesssetupX, 10,
+ 2 + userlen + passlen);
+
+ WSET(server->packet, smb_vwv0, 0x00ff);
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, given_max_xmit);
+ WSET(server->packet, smb_vwv3, 2);
+ WSET(server->packet, smb_vwv4, server->pid);
+ DSET(server->packet, smb_vwv5, server->sesskey);
+ WSET(server->packet, smb_vwv7, passlen + 1);
+ WSET(server->packet, smb_vwv8, 0);
+ WSET(server->packet, smb_vwv9, 0);
+
+ p = SMB_BUF(server->packet);
+ strcpy(p, server->m.password);
+ p += passlen + 1;
+ strcpy(p, server->m.username);
+ }
+
+ if ((result = smb_request_ok(server, SMBsesssetupX, 3, 0)) < 0)
+ {
+ DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ smb_decode_word(server->packet + 32, &(server->server_uid));
+ } else
+ {
+ server->max_xmit = 0;
+ server->maxmux = 0;
+ server->maxvcs = 0;
+ server->blkmode = 0;
+ server->sesskey = 0;
+ }
+
+ /* Fine! We have a connection, send a tcon message. */
+
+ smb_setup_header(server, SMBtcon, 0,
+ 6 + strlen(server->m.service) +
+ strlen(server->m.password) + strlen(dev));
+
+ p = SMB_BUF(server->packet);
+ p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
+ p = smb_encode_ascii(p, server->m.password, strlen(server->m.password));
+ p = smb_encode_ascii(p, dev, strlen(dev));
+
+ if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0)
+ {
+ DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ DDPRINTK("OK! Managed to set up SMBtcon!\n");
+
+ p = SMB_VWV(server->packet);
+
+ if (server->protocol <= PROTOCOL_COREPLUS)
+ {
+ word max_xmit;
+
+ p = smb_decode_word(p, &max_xmit);
+ server->max_xmit = max_xmit;
+
+ if (server->max_xmit > given_max_xmit)
+ {
+ server->max_xmit = given_max_xmit;
+ }
+ } else
+ {
+ p += 2;
+ }
+
+ p = smb_decode_word(p, &server->tid);
+
+ /* Ok, everything is fine. max_xmit does not include */
+ /* the TCP-SMB header of 4 bytes. */
+ server->max_xmit += 4;
+
+ DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
+
+ /* Now make a new packet with the correct size. */
+ smb_vfree(server->packet);
+
+ server->packet = smb_vmalloc(server->max_xmit);
+ if (server->packet == NULL)
+ {
+ printk("smb_proc_connect: No memory left in end of "
+ "connection phase :-(\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ server->packet_size = server->max_xmit;
+
+ DPRINTK("smb_proc_connect: Normal exit\n");
+ return 0;
+
+ fail:
+ server->state = CONN_INVALID;
+ return result;
+}
+
+/* smb_proc_reconnect: server->packet is allocated with
+ server->max_xmit bytes if and only if we return >= 0 */
+int
+smb_proc_connect(struct smb_server *server)
+{
+ int result;
+ smb_lock_server(server);
+
+ result = smb_proc_reconnect(server);
+
+ if ((result < 0) && (server->packet != NULL))
+ {
+ smb_vfree(server->packet);
+ server->packet = NULL;
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_disconnect(struct smb_server *server)
+{
+ smb_setup_header_exclusive(server, SMBtdis, 0, 0);
+ return smb_request_ok_unlock(server, SMBtdis, 0, 0);
+}
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
new file mode 100644
index 000000000..ca6d8c269
--- /dev/null
+++ b/fs/smbfs/sock.c
@@ -0,0 +1,714 @@
+/*
+ * sock.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+
+#include <linux/smb.h>
+#include <linux/smbno.h>
+
+#include <asm/uaccess.h>
+
+#define _S(nr) (1<<((nr)-1))
+
+static int
+_recvfrom(struct socket *sock, unsigned char *ubuf, int size,
+ int noblock, unsigned flags, struct sockaddr_in *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
+_send(struct socket *sock, const void *buff, int len,
+ int nonblock, unsigned flags)
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = (void *) buff;
+ iov.iov_len = len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+}
+
+static void
+smb_data_callback(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char peek_buf[4];
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *) peek_buf, 1, 1,
+ MSG_PEEK, NULL, NULL);
+
+ while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
+ {
+ /* got SESSION KEEP ALIVE */
+ result = _recvfrom(sock, (void *) peek_buf,
+ 4, 1, 0, NULL, NULL);
+
+ DDPRINTK("smb_data_callback:"
+ " got SESSION KEEP ALIVE\n");
+
+ if (result == -EAGAIN)
+ {
+ break;
+ }
+ result = _recvfrom(sock, (void *) peek_buf,
+ 1, 1, MSG_PEEK,
+ NULL, NULL);
+ }
+ set_fs(fs);
+
+ if (result != -EAGAIN)
+ {
+ wake_up_interruptible(sk->sleep);
+ }
+ }
+}
+
+int
+smb_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ((server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("smb_catch_keepalive: did not get valid server!\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM)
+ {
+ printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ sk = (struct sock *) (sock->data);
+
+ if (sk == NULL)
+ {
+ printk("smb_catch_keepalive: sk == NULL");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int) (sk->data_ready),
+ (unsigned int) (server->data_ready));
+
+ if (sk->data_ready == smb_data_callback)
+ {
+ printk("smb_catch_keepalive: already done\n");
+ return -EINVAL;
+ }
+ server->data_ready = sk->data_ready;
+ sk->data_ready = smb_data_callback;
+ return 0;
+}
+
+int
+smb_dont_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ((server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("smb_dont_catch_keepalive: "
+ "did not get valid server!\n");
+ return -EINVAL;
+ }
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM)
+ {
+ printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
+ return -EINVAL;
+ }
+ sk = (struct sock *) (sock->data);
+
+ if (sk == NULL)
+ {
+ printk("smb_dont_catch_keepalive: sk == NULL");
+ return -EINVAL;
+ }
+ if (server->data_ready == NULL)
+ {
+ printk("smb_dont_catch_keepalive: "
+ "server->data_ready == NULL\n");
+ return -EINVAL;
+ }
+ if (sk->data_ready != smb_data_callback)
+ {
+ printk("smb_dont_catch_keepalive: "
+ "sk->data_callback != smb_data_callback\n");
+ return -EINVAL;
+ }
+ DDPRINTK("smb_dont_catch_keepalive: 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;
+ server->data_ready = NULL;
+ return 0;
+}
+
+static int
+smb_send_raw(struct socket *sock, unsigned char *source, int length)
+{
+ int result;
+ int already_sent = 0;
+
+ while (already_sent < length)
+ {
+ result = _send(sock,
+ (void *) (source + already_sent),
+ length - already_sent, 0, 0);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_send_raw: sendto error = %d\n",
+ -result);
+ return result;
+ }
+ already_sent += result;
+ }
+ return already_sent;
+}
+
+static int
+smb_receive_raw(struct socket *sock, unsigned char *target, int length)
+{
+ int result;
+ int already_read = 0;
+
+ while (already_read < length)
+ {
+ result = _recvfrom(sock,
+ (void *) (target + already_read),
+ length - already_read, 0, 0,
+ NULL, NULL);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_receive_raw: recvfrom error = %d\n",
+ -result);
+ return result;
+ }
+ already_read += result;
+ }
+ return already_read;
+}
+
+static int
+smb_get_length(struct socket *sock, unsigned char *header)
+{
+ int result;
+ unsigned char peek_buf[4];
+ unsigned short fs;
+
+ re_recv:
+ fs = get_fs();
+ set_fs(get_ds());
+ result = smb_receive_raw(sock, peek_buf, 4);
+ set_fs(fs);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_get_length: recv error = %d\n", -result);
+ return result;
+ }
+ switch (peek_buf[0])
+ {
+ case 0x00:
+ case 0x82:
+ break;
+
+ case 0x85:
+ DPRINTK("smb_get_length: Got SESSION KEEP ALIVE\n");
+ goto re_recv;
+
+ default:
+ printk("smb_get_length: Invalid NBT packet\n");
+ return -EIO;
+ }
+
+ if (header != NULL)
+ {
+ memcpy(header, peek_buf, 4);
+ }
+ /* The length in the RFC NB header is the raw data length */
+ return smb_len(peek_buf);
+}
+
+static struct socket *
+server_sock(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+
+ if (server == NULL)
+ return NULL;
+ if ((file = server->sock_file) == NULL)
+ return NULL;
+ if ((inode = file->f_inode) == NULL)
+ return NULL;
+ return &(inode->u.socket_i);
+}
+
+/*
+ * smb_receive
+ * fs points to the correct segment
+ */
+static int
+smb_receive(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ int len;
+ int result;
+ unsigned char peek_buf[4];
+
+ len = smb_get_length(sock, peek_buf);
+
+ if (len < 0)
+ {
+ return len;
+ }
+ if (len + 4 > server->packet_size)
+ {
+ /* Some servers do not care about our max_xmit. They
+ send larger packets */
+ DPRINTK("smb_receive: Increase packet size from %d to %d\n",
+ server->packet_size, len + 4);
+ smb_vfree(server->packet);
+ server->packet_size = 0;
+ server->packet = smb_vmalloc(len + 4);
+ if (server->packet == NULL)
+ {
+ return -ENOMEM;
+ }
+ server->packet_size = len + 4;
+ }
+ memcpy(server->packet, peek_buf, 4);
+ result = smb_receive_raw(sock, server->packet + 4, len);
+
+ if (result < 0)
+ {
+ printk("smb_receive: receive error: %d\n", result);
+ return result;
+ }
+ server->rcls = BVAL(server->packet, 9);
+ server->err = WVAL(server->packet, 11);
+
+ if (server->rcls != 0)
+ {
+ DPRINTK("smb_receive: rcls=%d, err=%d\n",
+ server->rcls, server->err);
+ }
+ return result;
+}
+
+static int
+smb_receive_trans2(struct smb_server *server,
+ int *ldata, unsigned char **data,
+ int *lparam, unsigned char **param)
+{
+ int total_data = 0;
+ int total_param = 0;
+ int result;
+ unsigned char *inbuf = server->packet;
+ unsigned char *rcv_buf;
+ int buf_len;
+ int data_len = 0;
+ int param_len = 0;
+
+ if ((result = smb_receive(server)) < 0)
+ {
+ return result;
+ }
+ if (server->rcls != 0)
+ {
+ *param = *data = server->packet;
+ *ldata = *lparam = 0;
+ return 0;
+ }
+ total_data = WVAL(inbuf, smb_tdrcnt);
+ total_param = WVAL(inbuf, smb_tprcnt);
+
+ DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
+
+ if ((total_data > TRANS2_MAX_TRANSFER)
+ || (total_param > TRANS2_MAX_TRANSFER))
+ {
+ DPRINTK("smb_receive_trans2: data/param too long\n");
+ return -EIO;
+ }
+ buf_len = total_data + total_param;
+ if (server->packet_size > buf_len)
+ {
+ buf_len = server->packet_size;
+ }
+ if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
+ {
+ DPRINTK("smb_receive_trans2: could not alloc data area\n");
+ return -ENOMEM;
+ }
+ *param = rcv_buf;
+ *data = rcv_buf + total_param;
+
+ while (1)
+ {
+ if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
+ > total_param)
+ {
+ DPRINTK("smb_receive_trans2: invalid parameters\n");
+ result = -EIO;
+ goto fail;
+ }
+ memcpy(*param + WVAL(inbuf, smb_prdisp),
+ smb_base(inbuf) + WVAL(inbuf, smb_proff),
+ WVAL(inbuf, smb_prcnt));
+ param_len += WVAL(inbuf, smb_prcnt);
+
+ if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
+ > total_data)
+ {
+ DPRINTK("smb_receive_trans2: invalid data block\n");
+ result = -EIO;
+ goto fail;
+ }
+ DDPRINTK("target: %X\n", *data + WVAL(inbuf, smb_drdisp));
+ DDPRINTK("source: %X\n",
+ smb_base(inbuf) + WVAL(inbuf, smb_droff));
+ DDPRINTK("disp: %d, off: %d, cnt: %d\n",
+ WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
+ WVAL(inbuf, smb_drcnt));
+
+ memcpy(*data + WVAL(inbuf, smb_drdisp),
+ smb_base(inbuf) + WVAL(inbuf, smb_droff),
+ WVAL(inbuf, smb_drcnt));
+ data_len += WVAL(inbuf, smb_drcnt);
+
+ if ((WVAL(inbuf, smb_tdrcnt) > total_data)
+ || (WVAL(inbuf, smb_tprcnt) > total_param))
+ {
+ printk("smb_receive_trans2: data/params grew!\n");
+ result = -EIO;
+ goto fail;
+ }
+ /* the total lengths might shrink! */
+ total_data = WVAL(inbuf, smb_tdrcnt);
+ total_param = WVAL(inbuf, smb_tprcnt);
+
+ if ((data_len >= total_data) && (param_len >= total_param))
+ {
+ break;
+ }
+ if ((result = smb_receive(server)) < 0)
+ {
+ goto fail;
+ }
+ if (server->rcls != 0)
+ {
+ result = -EIO;
+ goto fail;
+ }
+ }
+ *ldata = data_len;
+ *lparam = param_len;
+
+ smb_vfree(server->packet);
+ server->packet = rcv_buf;
+ server->packet_size = buf_len;
+ return 0;
+
+ fail:
+ smb_vfree(rcv_buf);
+ return result;
+}
+
+int
+smb_release(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ int result;
+
+ if (sock == NULL)
+ {
+ return -EINVAL;
+ }
+ result = sock->ops->release(sock, NULL);
+ DPRINTK("smb_release: sock->ops->release = %d\n", result);
+
+ /* inet_release does not set sock->state. Maybe someone is
+ confused about sock->state being SS_CONNECTED while there
+ is nothing behind it, so I set it to SS_UNCONNECTED. */
+ sock->state = SS_UNCONNECTED;
+
+ result = sock->ops->create(sock, 0);
+ DPRINTK("smb_release: sock->ops->create = %d\n", result);
+ return result;
+}
+
+int
+smb_connect(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ if (sock == NULL)
+ {
+ return -EINVAL;
+ }
+ if (sock->state != SS_UNCONNECTED)
+ {
+ DPRINTK("smb_connect: socket is not unconnected: %d\n",
+ sock->state);
+ }
+ return sock->ops->connect(sock, (struct sockaddr *) &(server->m.addr),
+ sizeof(struct sockaddr_in), 0);
+}
+
+int
+smb_request(struct smb_server *server)
+{
+ unsigned long old_mask;
+ unsigned short fs;
+ int len, result;
+
+ unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+ if (buffer == NULL)
+ {
+ printk("smb_request: Bad server!\n");
+ return -EBADF;
+ }
+ if (server->state != CONN_VALID)
+ {
+ return -EIO;
+ }
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+ len = smb_len(buffer) + 4;
+
+ DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_raw(server_sock(server), (void *) buffer, len);
+ if (result > 0)
+ {
+ result = smb_receive(server);
+ }
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ result = result2;
+ }
+ }
+ if (result < 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+ DDPRINTK("smb_request: result = %d\n", result);
+
+ return result;
+}
+
+#define ROUND_UP(x) (((x)+3) & ~3)
+static int
+smb_send_trans2(struct smb_server *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param)
+{
+ struct socket *sock = server_sock(server);
+
+ /* I know the following is very ugly, but I want to build the
+ smb packet as efficiently as possible. */
+
+ const int smb_parameters = 15;
+ const int oparam =
+ ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3);
+ const int odata =
+ ROUND_UP(oparam + lparam);
+ const int bcc =
+ odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2);
+ const int packet_length =
+ SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2;
+
+ unsigned char padding[4] =
+ {0,};
+ char *p;
+
+ struct iovec iov[4];
+ struct msghdr msg;
+
+ if ((bcc + oparam) > server->max_xmit)
+ {
+ return -ENOMEM;
+ }
+ p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
+
+ WSET(server->packet, smb_tpscnt, lparam);
+ WSET(server->packet, smb_tdscnt, ldata);
+ WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
+ WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
+ WSET(server->packet, smb_msrcnt, 0);
+ WSET(server->packet, smb_flags, 0);
+ DSET(server->packet, smb_timeout, 0);
+ WSET(server->packet, smb_pscnt, lparam);
+ WSET(server->packet, smb_psoff, oparam - 4);
+ WSET(server->packet, smb_dscnt, ldata);
+ WSET(server->packet, smb_dsoff, odata - 4);
+ WSET(server->packet, smb_suwcnt, 1);
+ WSET(server->packet, smb_setup0, trans2_command);
+ *p++ = 0; /* null smb_name for trans2 */
+ *p++ = 'D'; /* this was added because OS/2 does it */
+ *p++ = ' ';
+
+ iov[0].iov_base = (void *) server->packet;
+ iov[0].iov_len = oparam;
+ iov[1].iov_base = (param == NULL) ? padding : param;
+ iov[1].iov_len = lparam;
+ iov[2].iov_base = padding;
+ iov[2].iov_len = odata - oparam - lparam;
+ iov[3].iov_base = (data == NULL) ? padding : data;
+ iov[3].iov_len = ldata;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 4;
+
+ return sock->ops->sendmsg(sock, &msg, packet_length, 0, 0);
+}
+
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */
+int
+smb_trans2_request(struct smb_server *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param,
+ int *lrdata, unsigned char **rdata,
+ int *lrparam, unsigned char **rparam)
+{
+ unsigned long old_mask;
+ unsigned short fs;
+ int result;
+
+ DDPRINTK("smb_trans2_request: com=%d, ld=%d, lp=%d\n",
+ trans2_command, ldata, lparam);
+
+ if (server->state != CONN_VALID)
+ {
+ return -EIO;
+ }
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_trans2(server, trans2_command,
+ ldata, data, lparam, param);
+ if (result >= 0)
+ {
+ result = smb_receive_trans2(server,
+ lrdata, rdata, lrparam, rparam);
+ }
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ result = result2;
+ }
+ }
+ if (result < 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+ DDPRINTK("smb_trans2_request: result = %d\n", result);
+
+ return result;
+}