summaryrefslogtreecommitdiffstats
path: root/fs/smbfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
commit1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch)
tree141e31f89f18b9fe0831f31852e0435ceaccafc5 /fs/smbfs
parentfb9c690a18b3d66925a65b17441c37fa14d4370b (diff)
Merge with 2.4.0-test7.
Diffstat (limited to 'fs/smbfs')
-rw-r--r--fs/smbfs/ChangeLog11
-rw-r--r--fs/smbfs/Makefile2
-rw-r--r--fs/smbfs/dir.c33
-rw-r--r--fs/smbfs/getopt.c61
-rw-r--r--fs/smbfs/getopt.h15
-rw-r--r--fs/smbfs/inode.c159
-rw-r--r--fs/smbfs/proc.c369
-rw-r--r--fs/smbfs/sock.c14
8 files changed, 531 insertions, 133 deletions
diff --git a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog
index 893890334..a57b1cc67 100644
--- a/fs/smbfs/ChangeLog
+++ b/fs/smbfs/ChangeLog
@@ -1,5 +1,16 @@
ChangeLog for smbfs.
+2000-08-14 Urban Widmark <urban@svenskatest.se>
+
+ * dir.c: support case sensitive shares
+ * inode.c: ascii mount options
+ * proc.c: check length of paths to avoid buffer overflow
+ * proc.c: don't do interruptable_sleep in smb_retry to avoid signal
+ problem/race.
+ * proc.c: O_RDONLY & smb_revalidate_inode fix (tail -f)
+ * proc.c: add nls support
+ * sock.c: attempt to fix smb_data_callback (avoid infinite loop)
+
2000-07-25 Urban Widmark <urban@svenskatest.se>
* proc.c: fix 3 places where bad server responses could cause an Oops.
diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
index 3ea59f6cc..825b0156a 100644
--- a/fs/smbfs/Makefile
+++ b/fs/smbfs/Makefile
@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := smbfs.o
-O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o
+O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o
M_OBJS := $(O_TARGET)
# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 807d8f2e3..b2d5c4099 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -14,6 +14,7 @@
#include <linux/ctype.h>
#include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
#include <linux/smbno.h>
#include "smb_debug.h"
@@ -66,12 +67,12 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
switch ((unsigned int) filp->f_pos)
{
case 0:
- if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
+ if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0)
goto out;
filp->f_pos = 1;
case 1:
if (filldir(dirent, "..", 2, 1,
- dentry->d_parent->d_inode->i_ino) < 0)
+ dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
goto out;
filp->f_pos = 2;
}
@@ -127,7 +128,7 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
}
if (filldir(dirent, entry->name, entry->len,
- filp->f_pos, entry->ino) < 0)
+ filp->f_pos, entry->ino, DT_UNKNOWN) < 0)
break;
filp->f_pos += 1;
}
@@ -142,7 +143,7 @@ out:
}
/*
- * Note: in order to allow the smbclient process to open the
+ * Note: in order to allow the smbmount process to open the
* mount point, we don't revalidate if conn_pid is NULL.
*/
static int
@@ -190,6 +191,13 @@ static struct dentry_operations smbfs_dentry_operations =
d_delete: smb_delete_dentry,
};
+static struct dentry_operations smbfs_dentry_operations_case =
+{
+ d_revalidate: smb_lookup_validate,
+ d_delete: smb_delete_dentry,
+};
+
+
/*
* This is the callback when the dcache has a lookup hit.
*/
@@ -249,8 +257,7 @@ smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b)
if (a->len != b->len)
goto out;
- for (i=0; i < a->len; i++)
- {
+ for (i=0; i < a->len; i++) {
if (tolower(a->name[i]) != tolower(b->name[i]))
goto out;
}
@@ -300,6 +307,7 @@ smb_lookup(struct inode *dir, struct dentry *dentry)
struct smb_fattr finfo;
struct inode *inode;
int error;
+ struct smb_sb_info *server;
error = -ENAMETOOLONG;
if (dentry->d_name.len > SMB_MAXNAMELEN)
@@ -315,15 +323,18 @@ smb_lookup(struct inode *dir, struct dentry *dentry)
inode = NULL;
if (error == -ENOENT)
goto add_entry;
- if (!error)
- {
+ if (!error) {
error = -EACCES;
finfo.f_ino = smb_invent_inos(1);
inode = smb_iget(dir->i_sb, &finfo);
- if (inode)
- {
+ if (inode) {
add_entry:
- dentry->d_op = &smbfs_dentry_operations;
+ server = server_from_dentry(dentry);
+ if (server->mnt->flags & SMB_MOUNT_CASE)
+ dentry->d_op = &smbfs_dentry_operations_case;
+ else
+ dentry->d_op = &smbfs_dentry_operations;
+
d_add(dentry, inode);
smb_renew_times(dentry);
error = 0;
diff --git a/fs/smbfs/getopt.c b/fs/smbfs/getopt.c
new file mode 100644
index 000000000..0c5d111b3
--- /dev/null
+++ b/fs/smbfs/getopt.c
@@ -0,0 +1,61 @@
+/*
+ * getopt.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "getopt.h"
+
+/**
+ * smb_getopt - option parser
+ * @caller: name of the caller, for error messages
+ * @options: the options string
+ * @opts: an array of &struct option entries controlling parser operations
+ * @optopt: output; will contain the current option
+ * @optarg: output; will contain the value (if one exists)
+ * @flag: output; may be NULL; should point to a long for or'ing flags
+ * @value: output; may be NULL; will be overwritten with the integer value
+ * of the current argument.
+ *
+ * Helper to parse options on the format used by mount ("a=b,c=d,e,f").
+ * Returns opts->val if a matching entry in the 'opts' array is found,
+ * 0 when no more tokens are found, -1 if an error is encountered.
+ */
+int smb_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value)
+{
+ char *token;
+ char *val;
+ int i;
+
+ if ( (token = strsep(options, ",")) == NULL)
+ return 0;
+ *optopt = token;
+
+ *optarg = NULL;
+ if ((val = strchr (token, '=')) != NULL) {
+ *val++ = 0;
+ if (value)
+ *value = simple_strtoul(val, NULL, 0);
+ *optarg = val;
+ }
+
+ for (i = 0; opts[i].name != NULL; i++) {
+ if (!strcmp(opts[i].name, token)) {
+ if (opts[i].has_arg && (!val || !*val)) {
+ printk("%s: the %s option requires an argument\n",
+ caller, token);
+ return -1;
+ }
+
+ if (flag && opts[i].flag)
+ *flag |= opts[i].flag;
+
+ return opts[i].val;
+ }
+ }
+ printk("%s: Unrecognized mount option %s\n", caller, token);
+ return -1;
+}
diff --git a/fs/smbfs/getopt.h b/fs/smbfs/getopt.h
new file mode 100644
index 000000000..691f21103
--- /dev/null
+++ b/fs/smbfs/getopt.h
@@ -0,0 +1,15 @@
+#ifndef _LINUX_GETOPT_H
+#define _LINUX_GETOPT_H
+
+struct option {
+ const char *name;
+ int has_arg;
+ unsigned long flag;
+ int val;
+};
+
+extern int smb_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value);
+
+#endif /* _LINUX_GETOPT_H */
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index d7cb992b8..708951949 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -7,6 +7,7 @@
* Please add a note about your changes to smbfs in the ChangeLog file.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -20,6 +21,7 @@
#include <linux/file.h>
#include <linux/dcache.h>
#include <linux/smp_lock.h>
+#include <linux/nls.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
@@ -29,6 +31,7 @@
#include <asm/uaccess.h>
#include "smb_debug.h"
+#include "getopt.h"
static void smb_delete_inode(struct inode *);
static void smb_put_super(struct super_block *);
@@ -282,6 +285,82 @@ smb_delete_inode(struct inode *ino)
clear_inode(ino);
}
+/* FIXME: flags and has_arg could probably be merged. */
+struct option opts[] = {
+ { "version", 1, 0, 'v' },
+ { "win95", 0, SMB_MOUNT_WIN95, 1 },
+ { "oldattr", 0, SMB_MOUNT_OLDATTR, 1 },
+ { "dirattr", 0, SMB_MOUNT_DIRATTR, 1 },
+ { "case", 0, SMB_MOUNT_CASE, 1 },
+ { "uid", 1, 0, 'u' },
+ { "gid", 1, 0, 'g' },
+ { "file_mode", 1, 0, 'f' },
+ { "dir_mode", 1, 0, 'd' },
+ { "iocharset", 1, 0, 'i' },
+ { "codepage", 1, 0, 'c' },
+ { NULL, 0, 0, 0}
+};
+
+static int
+parse_options(struct smb_mount_data_kernel *mnt, char *options)
+{
+ int c;
+ unsigned long flags;
+ unsigned long value;
+ char *optarg;
+ char *optopt;
+
+ flags = 0;
+ while ( (c = smb_getopt("smbfs", &options, opts,
+ &optopt, &optarg, &flags, &value)) > 0) {
+
+ VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : "<none>");
+
+ switch (c) {
+ case 1:
+ /* got a "flag" option */
+ break;
+ case 'v':
+ if (value != SMB_MOUNT_VERSION) {
+ printk ("smbfs: Bad mount version %ld, expected %d\n",
+ value, SMB_MOUNT_VERSION);
+ return 0;
+ }
+ mnt->version = value;
+ break;
+ case 'u':
+ mnt->uid = value;
+ break;
+ case 'g':
+ mnt->gid = value;
+ break;
+ case 'f':
+ mnt->file_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->file_mode |= S_IFREG;
+ break;
+ case 'd':
+ mnt->dir_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->dir_mode |= S_IFDIR;
+ break;
+ case 'i':
+ strncpy(mnt->codepage.local_name, optarg,
+ SMB_NLS_MAXNAMELEN);
+ break;
+ case 'c':
+ strncpy(mnt->codepage.remote_name, optarg,
+ SMB_NLS_MAXNAMELEN);
+ break;
+ default:
+ printk ("smbfs: Unrecognized mount option %s\n",
+ optopt);
+ return -1;
+ }
+ }
+ mnt->flags = flags;
+ return c;
+}
+
+
static void
smb_put_super(struct super_block *sb)
{
@@ -300,18 +379,32 @@ smb_put_super(struct super_block *sb)
kfree(sb->u.smbfs_sb.temp_buf);
if (server->packet)
smb_vfree(server->packet);
+
+ if(sb->u.smbfs_sb.remote_nls) {
+ unload_nls(sb->u.smbfs_sb.remote_nls);
+ sb->u.smbfs_sb.remote_nls = NULL;
+ }
+ if(sb->u.smbfs_sb.local_nls) {
+ unload_nls(sb->u.smbfs_sb.local_nls);
+ sb->u.smbfs_sb.local_nls = NULL;
+ }
}
struct super_block *
smb_read_super(struct super_block *sb, void *raw_data, int silent)
{
- struct smb_mount_data *mnt;
+ struct smb_mount_data_kernel *mnt;
+ struct smb_mount_data *oldmnt;
struct inode *root_inode;
struct smb_fattr root;
+ int ver;
if (!raw_data)
goto out_no_data;
- if (((struct smb_mount_data *) raw_data)->version != SMB_MOUNT_VERSION)
+
+ oldmnt = (struct smb_mount_data *) raw_data;
+ ver = oldmnt->version;
+ if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII)
goto out_wrong_data;
sb->s_blocksize = 1024; /* Eh... Is this correct? */
@@ -320,6 +413,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
sb->s_flags = 0;
sb->s_op = &smb_sops;
+ sb->u.smbfs_sb.mnt = NULL;
sb->u.smbfs_sb.sock_file = NULL;
init_MUTEX(&sb->u.smbfs_sb.sem);
init_waitqueue_head(&sb->u.smbfs_sb.wait);
@@ -332,30 +426,61 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
goto out_no_mem;
/* Allocate the global temp buffer */
- sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL);
+ sb->u.smbfs_sb.temp_buf = kmalloc(2*SMB_MAXPATHLEN + 20, GFP_KERNEL);
if (!sb->u.smbfs_sb.temp_buf)
goto out_no_temp;
+ /* Setup NLS stuff */
+ sb->u.smbfs_sb.remote_nls = NULL;
+ sb->u.smbfs_sb.local_nls = NULL;
+ sb->u.smbfs_sb.name_buf = sb->u.smbfs_sb.temp_buf + SMB_MAXPATHLEN + 20;
+
/* Allocate the mount data structure */
- mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
+ /* FIXME: merge this with the other malloc and get a whole page? */
+ mnt = kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
if (!mnt)
goto out_no_mount;
- *mnt = *((struct smb_mount_data *) raw_data);
- /* FIXME: passes config flags in high bits of file mode. Should be a
- separate flags field. (but smbmount includes kernel headers ...) */
- mnt->version = (mnt->file_mode >> 9);
- mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
- mnt->file_mode |= S_IFREG;
- mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
- mnt->dir_mode |= S_IFDIR;
sb->u.smbfs_sb.mnt = mnt;
+
+ memset(mnt, 0, sizeof(struct smb_mount_data_kernel));
+ strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
+ SMB_NLS_MAXNAMELEN);
+ strncpy(mnt->codepage.local_name, CONFIG_SMB_NLS_REMOTE,
+ SMB_NLS_MAXNAMELEN);
+
+ if (ver == SMB_MOUNT_OLDVERSION) {
+ mnt->version = oldmnt->version;
+
+ /* FIXME: is this enough to convert uid/gid's ? */
+ mnt->mounted_uid = oldmnt->mounted_uid;
+ mnt->uid = oldmnt->uid;
+ mnt->gid = oldmnt->gid;
+
+ mnt->file_mode =
+ oldmnt->file_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->dir_mode =
+ oldmnt->dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->file_mode |= S_IFREG;
+ mnt->dir_mode |= S_IFDIR;
+
+ mnt->flags = (oldmnt->file_mode >> 9);
+ } else {
+ if (parse_options(mnt, raw_data))
+ goto out_bad_option;
+
+ mnt->mounted_uid = current->uid;
+ }
+ smb_setcodepage(&sb->u.smbfs_sb, &mnt->codepage);
+ if (!sb->u.smbfs_sb.convert)
+ PARANOIA("convert funcptr was NULL!\n");
+
/*
* Display the enabled options
* Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2)
*/
- if (mnt->version & SMB_FIX_OLDATTR)
+ if (mnt->flags & SMB_MOUNT_OLDATTR)
printk("SMBFS: Using core getattr (Win 95 speedup)\n");
- else if (mnt->version & SMB_FIX_DIRATTR)
+ else if (mnt->flags & SMB_MOUNT_DIRATTR)
printk("SMBFS: Using dir ff getattr\n");
/*
@@ -374,16 +499,18 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
out_no_root:
iput(root_inode);
+out_bad_option:
kfree(sb->u.smbfs_sb.mnt);
out_no_mount:
kfree(sb->u.smbfs_sb.temp_buf);
out_no_temp:
smb_vfree(sb->u.smbfs_sb.packet);
out_no_mem:
- printk(KERN_ERR "smb_read_super: allocation failure\n");
+ if (!sb->u.smbfs_sb.mnt)
+ printk(KERN_ERR "smb_read_super: allocation failure\n");
goto out_fail;
out_wrong_data:
- printk(KERN_ERR "SMBFS: need mount version %d\n", SMB_MOUNT_VERSION);
+ printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver);
goto out_fail;
out_no_data:
printk(KERN_ERR "smb_read_super: missing data argument\n");
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index e969320b7..7863cd2da 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -16,6 +16,7 @@
#include <linux/fcntl.h>
#include <linux/dcache.h>
#include <linux/dirent.h>
+#include <linux/nls.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
@@ -25,10 +26,14 @@
#include "smb_debug.h"
+
/* Features. Undefine if they cause problems, this should perhaps be a
config option. */
#define SMBFS_POSIX_UNLINK 1
+/* Allow smb_retry to be interrupted. Not sure of the benefit ... */
+/* #define SMB_RETRY_INTR */
+
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
#define SMB_CMD(packet) (*(packet+8))
#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1))
@@ -48,6 +53,20 @@ static int
smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *fattr);
+
+static inline void
+smb_lock_server(struct smb_sb_info *server)
+{
+ down(&(server->sem));
+}
+
+static inline void
+smb_unlock_server(struct smb_sb_info *server)
+{
+ up(&(server->sem));
+}
+
+
static void
str_upper(char *name, int len)
{
@@ -83,6 +102,96 @@ static void reverse_string(char *buf, int len)
}
}
+/* no conversion, just a wrapper for memcpy. */
+static int convert_memcpy(char *output, int olen,
+ const char *input, int ilen,
+ struct nls_table *nls_from,
+ struct nls_table *nls_to)
+{
+ memcpy(output, input, ilen);
+ return ilen;
+}
+
+/* convert from one "codepage" to another (possibly being utf8). */
+static int convert_cp(char *output, int olen,
+ const char *input, int ilen,
+ struct nls_table *nls_from,
+ struct nls_table *nls_to)
+{
+ int len = 0;
+ int n;
+ wchar_t ch;
+
+ if (!nls_from || !nls_to) {
+ PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to);
+ return convert_memcpy(output, olen, input, ilen, NULL, NULL);
+ }
+
+ while (ilen > 0) {
+ /* convert by changing to unicode and back to the new cp */
+ n = nls_from->char2uni((unsigned char *)input, ilen, &ch);
+ if (n < 0)
+ goto out;
+ input += n;
+ ilen -= n;
+
+ n = nls_to->uni2char(ch, output, olen);
+ if (n < 0)
+ goto out;
+ output += n;
+ olen -= n;
+
+ len += n;
+ }
+out:
+ return len;
+}
+
+static int setcodepage(struct smb_sb_info *server,
+ struct nls_table **p, char *name)
+{
+ struct nls_table *nls;
+
+ if (!name || !*name) {
+ nls = NULL;
+ } else if ( (nls = load_nls(name)) == NULL) {
+ printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name);
+ return -EINVAL;
+ }
+
+ /* if already set, unload the previous one. */
+ if (*p)
+ unload_nls(*p);
+ *p = nls;
+
+ return 0;
+}
+
+/* Handles all changes to codepage settings. */
+int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
+{
+ int n;
+
+ smb_lock_server(server);
+
+ n = setcodepage(server, &server->local_nls, cp->local_name);
+ if (n != 0)
+ goto out;
+ n = setcodepage(server, &server->remote_nls, cp->remote_name);
+ if (n != 0)
+ setcodepage(server, &server->local_nls, NULL);
+
+out:
+ if (server->local_nls != NULL && server->remote_nls != NULL)
+ server->convert = convert_cp;
+ else
+ server->convert = convert_memcpy;
+
+ smb_unlock_server(server);
+ return n;
+}
+
+
/*****************************************************************************/
/* */
/* Encoding/Decoding section */
@@ -107,9 +216,11 @@ smb_encode_smb_length(__u8 * p, __u32 len)
* smb_build_path: build the path to entry and name storing it in buf.
* The path returned will have the trailing '\0'.
*/
-static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
+static int smb_build_path(struct smb_sb_info *server, char * buf,
+ struct dentry * entry, struct qstr * name)
{
char *path = buf;
+ int len;
if (entry == NULL)
goto test_name_and_out;
@@ -129,9 +240,16 @@ static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
* and store it in reversed order [see reverse_string()]
*/
for (;;) {
- memcpy(path, entry->d_name.name, entry->d_name.len);
- reverse_string(path, entry->d_name.len);
- path += entry->d_name.len;
+ if (entry->d_name.len > SMB_MAXNAMELEN)
+ return -ENAMETOOLONG;
+ if (path - buf + entry->d_name.len > SMB_MAXPATHLEN)
+ return -ENAMETOOLONG;
+
+ len = server->convert(path, SMB_MAXNAMELEN,
+ entry->d_name.name, entry->d_name.len,
+ server->local_nls, server->remote_nls);
+ reverse_string(path, len);
+ path += len;
*(path++) = '\\';
@@ -147,25 +265,28 @@ test_name_and_out:
if (name != NULL) {
*(path++) = '\\';
name_and_out:
- memcpy(path, name->name, name->len);
- path += name->len;
+ len = server->convert(path, SMB_MAXNAMELEN,
+ name->name, name->len,
+ server->local_nls, server->remote_nls);
+ path += len;
}
out:
*(path++) = '\0';
return (path-buf);
}
-static char *smb_encode_path(struct smb_sb_info *server, char *buf,
- struct dentry *dir, struct qstr *name)
+static int smb_encode_path(struct smb_sb_info *server, char *buf,
+ struct dentry *dir, struct qstr *name)
{
- char *start = buf;
-
- buf += smb_build_path(dir, name, buf);
+ int result;
+ result = smb_build_path(server, buf, dir, name);
+ if (result < 0)
+ goto out;
if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
- str_upper(start, buf - start);
-
- return buf;
+ str_upper(buf, result);
+out:
+ return result;
}
/* The following are taken directly from msdos-fs */
@@ -464,18 +585,6 @@ err_unknown:
return EIO;
}
-static inline void
-smb_lock_server(struct smb_sb_info *server)
-{
- down(&(server->sem));
-}
-
-static inline void
-smb_unlock_server(struct smb_sb_info *server)
-{
- up(&(server->sem));
-}
-
/*
* smb_retry: This function should be called when smb_request_ok has
* indicated an error. If the error was indicated because the
@@ -495,8 +604,7 @@ smb_retry(struct smb_sb_info *server)
smb_close_socket(server);
- if (pid == 0)
- {
+ if (pid == 0) {
printk(KERN_ERR "smb_retry: no connection process\n");
server->state = CONN_RETRIED;
goto out;
@@ -511,26 +619,35 @@ smb_retry(struct smb_sb_info *server)
* Note: use the "priv" flag, as a user process may need to reconnect.
*/
error = kill_proc(pid, SIGUSR1, 1);
- if (error)
- {
+ if (error) {
printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error);
goto out_restore;
}
- VERBOSE("signalled pid %d, waiting for new connection\n",
- server->conn_pid);
+ VERBOSE("signalled pid %d, waiting for new connection\n", pid);
/*
* Wait for the new connection.
*/
+#ifdef SMB_RETRY_INTR
interruptible_sleep_on_timeout(&server->wait, 5*HZ);
if (signal_pending(current))
printk(KERN_INFO "smb_retry: caught signal\n");
+#else
+ /*
+ * We don't want to be interrupted. For example, what if 'current'
+ * already has recieved a signal? sleep_on would terminate immediately
+ * and smbmount would not be able to re-establish connection.
+ *
+ * smbmount should be able to reconnect later, but it can't because
+ * it will get an -EIO on attempts to open the mountpoint!
+ */
+ sleep_on_timeout(&server->wait, 5*HZ);
+#endif
/*
* Check for a valid connection.
*/
- if (server->state == CONN_VALID)
- {
+ if (server->state == CONN_VALID) {
/* This should be changed to VERBOSE, except many smbfs
problems is with the userspace daemon not reconnecting. */
PARANOIA("sucessful, new pid=%d, generation=%d\n",
@@ -656,7 +773,7 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
if (server->opt.protocol == SMB_PROTOCOL_NT1 &&
(server->opt.max_xmit < 0x1000) &&
!(server->opt.capabilities & SMB_CAP_NT_SMBS)) {
- server->mnt->version |= SMB_FIX_WIN95;
+ server->mnt->flags |= SMB_MOUNT_WIN95;
#ifdef SMBFS_DEBUG_VERBOSE
printk(KERN_NOTICE "smb_newconn: detected WIN95 server\n");
#endif
@@ -667,7 +784,11 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
server->opt.capabilities);
out:
+#ifdef SMB_RETRY_INTR
wake_up_interruptible(&server->wait);
+#else
+ wake_up(&server->wait);
+#endif
return error;
out_putf:
@@ -738,7 +859,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
{
struct inode *ino = dentry->d_inode;
int mode, read_write = 0x42, read_only = 0x40;
- int error;
+ int res;
char *p;
/*
@@ -748,6 +869,9 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
mode = read_only;
#if 0
+ /* FIXME: why is this code not in? below we fix it so that a caller
+ wanting RO doesn't get RW. smb_revalidate_inode does some
+ optimization based on access mode. tail -f needs it to be correct. */
if (!(wish & (O_WRONLY | O_RDWR)))
mode = read_only;
#endif
@@ -757,20 +881,23 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
WSET(server->packet, smb_vwv0, mode);
WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
*p++ = 4;
- p = smb_encode_path(server, p, dentry, NULL);
+ res = smb_encode_path(server, p, dentry, NULL);
+ if (res < 0)
+ goto out;
+ p += res;
+
smb_setup_bcc(server, p);
- error = smb_request_ok(server, SMBopen, 7, 0);
- if (error != 0)
- {
+ res = smb_request_ok(server, SMBopen, 7, 0);
+ if (res != 0) {
if (smb_retry(server))
goto retry;
if (mode == read_write &&
- (error == -EACCES || error == -ETXTBSY || error == -EROFS))
+ (res == -EACCES || res == -ETXTBSY || res == -EROFS))
{
VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n",
- DENTRY_PATH(dentry), error);
+ DENTRY_PATH(dentry), res);
mode = read_only;
goto retry;
}
@@ -783,10 +910,12 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
/* smb_vwv2 has mtime */
/* smb_vwv4 has size */
ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK);
+ if (!(wish & (O_WRONLY | O_RDWR)))
+ ino->u.smbfs_i.access = SMB_O_RDONLY;
ino->u.smbfs_i.open = server->generation;
out:
- return error;
+ return res;
}
/*
@@ -1025,7 +1154,7 @@ smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid)
{
struct smb_sb_info *server = server_from_dentry(dentry);
char *p;
- int error;
+ int result;
smb_lock_server(server);
@@ -1034,22 +1163,24 @@ smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid)
WSET(server->packet, smb_vwv0, attr);
DSET(server->packet, smb_vwv1, utc2local(server, ctime));
*p++ = 4;
- p = smb_encode_path(server, p, dentry, NULL);
+ result = smb_encode_path(server, p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
smb_setup_bcc(server, p);
- error = smb_request_ok(server, SMBcreate, 1, 0);
- if (error < 0)
- {
+ result = smb_request_ok(server, SMBcreate, 1, 0);
+ if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
*fileid = WVAL(server->packet, smb_vwv0);
- error = 0;
+ result = 0;
out:
smb_unlock_server(server);
- return error;
+ return result;
}
int
@@ -1064,14 +1195,22 @@ smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry)
retry:
p = smb_setup_header(server, SMBmv, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
+
*p++ = 4;
- p = smb_encode_path(server, p, old_dentry, NULL);
+ result = smb_encode_path(server, p, old_dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
+
*p++ = 4;
- p = smb_encode_path(server, p, new_dentry, NULL);
+ result = smb_encode_path(server, p, new_dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
+
smb_setup_bcc(server, p);
- if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
- {
+ if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -1097,12 +1236,14 @@ smb_proc_generic_command(struct dentry *dentry, __u8 command)
retry:
p = smb_setup_header(server, command, 0, 0);
*p++ = 4;
- p = smb_encode_path(server, p, dentry, NULL);
+ result = smb_encode_path(server, p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
smb_setup_bcc(server, p);
result = smb_request_ok(server, command, 0, 0);
- if (result < 0)
- {
+ if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -1165,11 +1306,13 @@ smb_proc_unlink(struct dentry *dentry)
p = smb_setup_header(server, SMBunlink, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
*p++ = 4;
- p = smb_encode_path(server, p, dentry, NULL);
+ result = smb_encode_path(server, p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
smb_setup_bcc(server, p);
- if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
- {
+ if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
#if SMBFS_POSIX_UNLINK
if (result == -EACCES && !flag) {
/* Posix semantics is for the read-only state
@@ -1220,8 +1363,7 @@ smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length)
*p++ = 0;
smb_setup_bcc(server, p);
- if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
- {
+ if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -1306,6 +1448,8 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
len--;
entry->len = len;
+ /* FIXME: These only work for ascii chars, and recent smbmount doesn't
+ allow the flag to be set anyway. Remove? */
switch (server->opt.case_handling) {
case SMB_CASE_UPPER:
str_upper(entry->name, len);
@@ -1316,7 +1460,13 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
default:
break;
}
- DEBUG1("len=%d, name=%.*s\n", len, len, entry->name);
+
+ entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ entry->name, len,
+ server->remote_nls, server->local_nls);
+ entry->name = server->name_buf;
+
+ DEBUG1("len=%d, name=%.*s\n", entry->len, entry->len, entry->name);
return p + 22;
}
@@ -1355,7 +1505,10 @@ retry:
WSET(server->packet, smb_vwv1, aDIR);
*p++ = 4;
if (first == 1) {
- p = smb_encode_path(server, p, dir, &mask);
+ result = smb_encode_path(server, p, dir, &mask);
+ if (result < 0)
+ goto unlock_return;
+ p += result;
*p++ = 5;
WSET(p, 0, 0);
p += 2;
@@ -1467,12 +1620,11 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
switch (level) {
case 1:
len = *((unsigned char *) p + 22);
- entry->len = len;
entry->name = p + 23;
result = p + 24 + len;
VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
- p, entry->len, entry->len, entry->name);
+ p, len, len, entry->name);
break;
case 260:
result = p + WVAL(p, 0);
@@ -1482,14 +1634,14 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
entry->name = p + 94;
if (len && entry->name[len-1] == '\0')
len--;
- entry->len = len;
VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
- p, entry->len, entry->len, entry->name);
+ p, len, len, entry->name);
break;
default:
PARANOIA("Unknown info level %d\n", level);
result = p + WVAL(p, 0);
+ goto out;
}
switch (server->opt.case_handling) {
@@ -1503,6 +1655,11 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
break;
}
+ entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ entry->name, len,
+ server->remote_nls, server->local_nls);
+ entry->name = server->name_buf;
+out:
return result;
}
@@ -1561,7 +1718,12 @@ retry:
* Encode the initial path
*/
mask = param + 12;
- mask_len = smb_encode_path(server, mask, dir, &star) - mask;
+
+ mask_len = smb_encode_path(server, mask, dir, &star);
+ if (mask_len < 0) {
+ entries = mask_len;
+ goto unlock_return;
+ }
first = 1;
VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask);
@@ -1753,7 +1915,11 @@ smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
int mask_len, result;
retry:
- mask_len = smb_encode_path(server, mask, dentry, NULL) - mask;
+ mask_len = smb_encode_path(server, mask, dentry, NULL);
+ if (mask_len < 0) {
+ result = mask_len;
+ goto out;
+ }
VERBOSE("name=%s, len=%d\n", mask, mask_len);
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
WSET(param, 2, 1); /* max count */
@@ -1828,7 +1994,10 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
retry:
p = smb_setup_header(server, SMBgetatr, 0, 0);
*p++ = 4;
- p = smb_encode_path(server, p, dir, NULL);
+ result = smb_encode_path(server, p, dir, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
smb_setup_bcc(server, p);
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
@@ -1874,7 +2043,10 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
- p = smb_encode_path(server, param + 6, dir, NULL);
+ result = smb_encode_path(server, param + 6, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
0, NULL, p - param, param,
@@ -1905,7 +2077,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
* Kludge alert: Win 95 swaps the date and time field,
* contrary to the CIFS docs and Win NT practice.
*/
- if (server->mnt->version & SMB_FIX_WIN95) {
+ if (server->mnt->flags & SMB_MOUNT_WIN95) {
off_date = 2;
off_time = 0;
}
@@ -1945,21 +2117,16 @@ smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir,
/*
* Select whether to use core or trans2 getattr.
+ * Win 95 appears to break with the trans2 getattr.
*/
- if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) {
- /*
- * Win 95 appears to break with the trans2 getattr.
- * Note: mnt->version options are set at mount time (inode.c)
- */
- if (server->mnt->version & (SMB_FIX_OLDATTR|SMB_FIX_WIN95))
- goto core_attr;
- if (server->mnt->version & SMB_FIX_DIRATTR)
+ if (server->opt.protocol < SMB_PROTOCOL_LANMAN2 ||
+ (server->mnt->flags & (SMB_MOUNT_OLDATTR|SMB_MOUNT_WIN95)) ) {
+ result = smb_proc_getattr_core(server, dir, fattr);
+ } else {
+ if (server->mnt->flags & SMB_MOUNT_DIRATTR)
result = smb_proc_getattr_ff(server, dir, fattr);
else
result = smb_proc_getattr_trans2(server, dir, fattr);
- } else {
- core_attr:
- result = smb_proc_getattr_core(server, dir, fattr);
}
smb_finish_dirent(server, fattr);
@@ -2008,14 +2175,16 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
WSET(server->packet, smb_vwv6, 0);
WSET(server->packet, smb_vwv7, 0);
*p++ = 4;
- p = smb_encode_path(server, p, dentry, NULL);
+ result = smb_encode_path(server, p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p += result;
*p++ = 4;
*p++ = 0;
smb_setup_bcc(server, p);
result = smb_request_ok(server, SMBsetatr, 0, 0);
- if (result < 0)
- {
+ if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -2073,8 +2242,7 @@ smb_proc_setattr_ext(struct smb_sb_info *server,
#endif
result = smb_request_ok(server, SMBsetattrE, 0, 0);
- if (result < 0)
- {
+ if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -2107,7 +2275,10 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
- p = smb_encode_path(server, param + 6, dir, NULL);
+ result = smb_encode_path(server, param + 6, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
WSET(data, 0, 0); /* creation time */
WSET(data, 2, 0);
@@ -2170,8 +2341,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
smb_lock_server(server);
/* setting the time on a Win95 server fails (tridge) */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
- !(server->mnt->version & SMB_FIX_WIN95))
- {
+ !(server->mnt->flags & SMB_MOUNT_WIN95)) {
if (smb_is_open(inode) &&
inode->u.smbfs_i.access != SMB_O_RDONLY)
result = smb_proc_setattr_ext(server, inode, fattr);
@@ -2182,8 +2352,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
* Fail silently on directories ... timestamp can't be set?
*/
result = 0;
- if (S_ISREG(inode->i_mode))
- {
+ if (S_ISREG(inode->i_mode)) {
/*
* Set the mtime by opening and closing the file.
* Note that the file is opened read-only, but this
@@ -2192,8 +2361,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
result = -EACCES;
if (!smb_is_open(inode))
smb_proc_open(server, dentry, SMB_O_RDONLY);
- if (smb_is_open(inode))
- {
+ if (smb_is_open(inode)) {
inode->i_mtime = fattr->f_mtime;
result = smb_proc_close_inode(server, inode);
}
@@ -2208,7 +2376,7 @@ int
smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
{
struct smb_sb_info *server = &(sb->u.smbfs_sb);
- int error;
+ int result;
char *p;
smb_lock_server(server);
@@ -2216,8 +2384,7 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
retry:
smb_setup_header(server, SMBdskattr, 0, 0);
- if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
- {
+ if ((result = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
if (smb_retry(server))
goto retry;
goto out;
@@ -2226,11 +2393,11 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
attr->f_blocks = WVAL(p, 0);
attr->f_bsize = WVAL(p, 2) * WVAL(p, 4);
attr->f_bavail = attr->f_bfree = WVAL(p, 6);
- error = 0;
+ result = 0;
out:
smb_unlock_server(server);
- return error;
+ return result;
}
int
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
index 0c52bf871..01ae6ec87 100644
--- a/fs/smbfs/sock.c
+++ b/fs/smbfs/sock.c
@@ -111,12 +111,16 @@ smb_data_callback(void* ptr)
unsigned char peek_buf[4];
int result;
mm_segment_t fs;
+ int count = 100; /* this is a lot, we should have some data waiting */
+ int found = 0;
fs = get_fs();
set_fs(get_ds());
lock_kernel();
- while (1) {
+ while (count-- > 0) {
+ peek_buf[0] = 0;
+
result = -EIO;
if (job->sk->dead) {
PARANOIA("sock dead!\n");
@@ -125,7 +129,7 @@ smb_data_callback(void* ptr)
result = _recvfrom(socket, (void *) peek_buf, 1,
MSG_PEEK | MSG_DONTWAIT);
- if (result == -EAGAIN)
+ if (result < 0)
break;
if (peek_buf[0] != 0x85)
break;
@@ -136,13 +140,15 @@ smb_data_callback(void* ptr)
DEBUG1("got SESSION KEEPALIVE\n");
- if (result == -EAGAIN)
+ if (result < 0)
break;
+ found = 1;
}
unlock_kernel();
set_fs(fs);
- if (result != -EAGAIN)
+ DEBUG1("found=%d, count=%d, result=%d\n", found, count, result);
+ if (found)
found_data(job->sk);
kfree(ptr);
}