diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
commit | 1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch) | |
tree | 141e31f89f18b9fe0831f31852e0435ceaccafc5 /fs/smbfs | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'fs/smbfs')
-rw-r--r-- | fs/smbfs/ChangeLog | 11 | ||||
-rw-r--r-- | fs/smbfs/Makefile | 2 | ||||
-rw-r--r-- | fs/smbfs/dir.c | 33 | ||||
-rw-r--r-- | fs/smbfs/getopt.c | 61 | ||||
-rw-r--r-- | fs/smbfs/getopt.h | 15 | ||||
-rw-r--r-- | fs/smbfs/inode.c | 159 | ||||
-rw-r--r-- | fs/smbfs/proc.c | 369 | ||||
-rw-r--r-- | fs/smbfs/sock.c | 14 |
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); } |