summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZwane Mwaikambo <zwane@linuxpower.ca>2004-06-27 09:34:47 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-27 09:34:47 -0700
commitaa299b22c63d650b23c9e14e03eb77b8e3017312 (patch)
tree37d3f01bbecb66317394abc5697afc0ed2188f00
parent81c3dadecfdd71a2fff67fc43656490f8f0e29dd (diff)
[PATCH] Fix smbfs readdir oops
This has been reported a couple of times and is consistently causing some folks grief, so Urban, would you mind terribly if i send this patch to at least clear current bug reports. If there is additional stuff you want ontop of this let me know and i can send a follow up patch. The bug is that at times we haven't completed setting up the smb_ops so we have a temporary 'null' ops in place until the connection is completely up. With this setup it's possible to hit ->readdir() whilst the null ops are still in place, so we put the process to sleep until the connection setup is complete and then call the real ->readdir(). This patch addresses the bugzilla report at http://bugzilla.kernel.org/show_bug.cgi?id=1671 Signed-off-by: Zwane Mwaikambo <zwane@linuxpower.ca> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/smbfs/inode.c1
-rw-r--r--fs/smbfs/proc.c44
-rw-r--r--include/linux/smb_fs_sb.h3
3 files changed, 45 insertions, 3 deletions
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 4d4b5f40072d..c8ca98ab273e 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -521,6 +521,7 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
server->super_block = sb;
server->mnt = NULL;
server->sock_file = NULL;
+ init_waitqueue_head(&server->conn_wq);
init_MUTEX(&server->sem);
INIT_LIST_HEAD(&server->entry);
INIT_LIST_HEAD(&server->xmitq);
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index 3d67e86dcde2..da0f1642689a 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -56,6 +56,7 @@ static struct smb_ops smb_ops_os2;
static struct smb_ops smb_ops_win95;
static struct smb_ops smb_ops_winNT;
static struct smb_ops smb_ops_unix;
+static struct smb_ops smb_ops_null;
static void
smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
@@ -981,6 +982,9 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
smbiod_wake_up();
if (server->opt.capabilities & SMB_CAP_UNIX)
smb_proc_query_cifsunix(server);
+
+ server->conn_complete++;
+ wake_up_interruptible_all(&server->conn_wq);
return error;
out:
@@ -2794,10 +2798,45 @@ out:
}
static int
+smb_proc_ops_wait(struct smb_sb_info *server)
+{
+ int result;
+
+ result = wait_event_interruptible_timeout(server->conn_wq,
+ server->conn_complete, 30*HZ);
+
+ if (!result || signal_pending(current))
+ return -EIO;
+
+ return 0;
+}
+
+static int
smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir,
- struct smb_fattr *attr)
+ struct smb_fattr *fattr)
{
- return -EIO;
+ int result;
+
+ if (smb_proc_ops_wait(server) < 0)
+ return -EIO;
+
+ smb_init_dirent(server, fattr);
+ result = server->ops->getattr(server, dir, fattr);
+ smb_finish_dirent(server, fattr);
+
+ return result;
+}
+
+static int
+smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
+{
+ struct smb_sb_info *server = server_from_dentry(filp->f_dentry);
+
+ if (smb_proc_ops_wait(server) < 0)
+ return -EIO;
+
+ return server->ops->readdir(filp, dirent, filldir, ctl);
}
int
@@ -3431,6 +3470,7 @@ static struct smb_ops smb_ops_unix =
/* Place holder until real ops are in place */
static struct smb_ops smb_ops_null =
{
+ .readdir = smb_proc_readdir_null,
.getattr = smb_proc_getattr_null,
};
diff --git a/include/linux/smb_fs_sb.h b/include/linux/smb_fs_sb.h
index 976332af7949..5b4ae2cc445c 100644
--- a/include/linux/smb_fs_sb.h
+++ b/include/linux/smb_fs_sb.h
@@ -57,7 +57,8 @@ struct smb_sb_info {
unsigned int generation;
pid_t conn_pid;
struct smb_conn_opt opt;
-
+ wait_queue_head_t conn_wq;
+ int conn_complete;
struct semaphore sem;
unsigned char header[SMB_HEADER_LEN + 20*2 + 2];