Commit 37c55498 authored by Urban Widmark's avatar Urban Widmark Committed by Linus Torvalds

[PATCH] smbfs - smbiod

This patch for 2.5.25 is a rewrite of how smbfs builds requests. It allows
for more parallellism, better error handling and supporting oplocks with
further patches.
parent 145ecbd6
......@@ -4,7 +4,8 @@
obj-$(CONFIG_SMB_FS) += smbfs.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \
smbiod.o request.o
# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
# SMBFS_PARANOIA should normally be enabled.
......@@ -23,7 +24,7 @@ include $(TOPDIR)/Rules.make
#
# getopt.c not included. It is intentionally separate
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c
proto:
-rm -f proto.h
......@@ -31,5 +32,7 @@ proto:
@echo >> proto2.h " * Autogenerated with cproto on: " `date`
@echo >> proto2.h " */"
@echo >> proto2.h ""
@echo >> proto2.h "struct smb_request;"
@echo >> proto2.h ""
cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO -D__KERNEL__ $(SRC) >> proto2.h
mv proto2.h proto.h
......@@ -42,9 +42,7 @@ smb_fsync(struct file *file, struct dentry * dentry, int datasync)
* Note: this function requires all pages to have been written already
* (should be ok with writepage_sync)
*/
smb_lock_server(server);
result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid);
smb_unlock_server(server);
return result;
}
......@@ -129,7 +127,7 @@ smb_writepage_sync(struct inode *inode, struct page *page,
offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset;
VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n",
inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize);
inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize);
do {
if (count < wsize)
......
......@@ -434,17 +434,17 @@ smb_put_super(struct super_block *sb)
{
struct smb_sb_info *server = SMB_SB(sb);
if (server->sock_file) {
smb_dont_catch_keepalive(server);
fput(server->sock_file);
}
smbiod_unregister_server(server);
smb_lock_server(server);
server->state = CONN_INVALID;
smb_close_socket(server);
if (server->conn_pid)
kill_proc(server->conn_pid, SIGTERM, 1);
smb_kfree(server->ops);
if (server->packet)
smb_vfree(server->packet);
if (server->remote_nls) {
unload_nls(server->remote_nls);
......@@ -455,6 +455,7 @@ smb_put_super(struct super_block *sb)
server->local_nls = NULL;
}
sb->u.generic_sbp = NULL;
smb_unlock_server(server);
smb_kfree(server);
}
......@@ -491,32 +492,25 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
server->mnt = NULL;
server->sock_file = NULL;
init_MUTEX(&server->sem);
init_waitqueue_head(&server->wait);
INIT_LIST_HEAD(&server->entry);
INIT_LIST_HEAD(&server->xmitq);
INIT_LIST_HEAD(&server->recvq);
server->conn_error = 0;
server->conn_pid = 0;
server->state = CONN_INVALID; /* no connection yet */
server->generation = 0;
server->packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
server->packet = smb_vmalloc(server->packet_size);
if (!server->packet)
goto out_no_mem;
/* Allocate the global temp buffer and some superblock helper structs */
/* FIXME: move these to the smb_sb_info struct */
VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
2*SMB_MAXPATHLEN + 20);
sizeof(struct smb_mount_data_kernel));
mem = smb_kmalloc(sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
2*SMB_MAXPATHLEN + 20, GFP_KERNEL);
sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
if (!mem)
goto out_no_temp;
goto out_no_mem;
server->ops = mem;
server->mnt = mem + sizeof(struct smb_ops);
server->name_buf = mem + sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel);
server->temp_buf = mem + sizeof(struct smb_ops) +
sizeof(struct smb_mount_data_kernel) +
SMB_MAXPATHLEN + 1;
/* Setup NLS stuff */
server->remote_nls = NULL;
......@@ -573,14 +567,14 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
goto out_no_root;
smb_new_dentry(sb->s_root);
smbiod_register_server(server);
return 0;
out_no_root:
iput(root_inode);
out_bad_option:
smb_kfree(mem);
out_no_temp:
smb_vfree(server->packet);
out_no_mem:
if (!server->mnt)
printk(KERN_ERR "smb_fill_super: allocation failure\n");
......@@ -764,14 +758,19 @@ static int __init init_smb_fs(void)
err = init_inodecache();
if (err)
goto out1;
goto out_inode;
err = smb_init_request_cache();
if (err)
goto out_request;
err = register_filesystem(&smb_fs_type);
if (err)
goto out;
return 0;
out:
smb_destroy_request_cache();
out_request:
destroy_inodecache();
out1:
out_inode:
return err;
}
......@@ -779,6 +778,7 @@ static void __exit exit_smb_fs(void)
{
DEBUG1("unregistering ...\n");
unregister_filesystem(&smb_fs_type);
smb_destroy_request_cache();
destroy_inodecache();
#ifdef DEBUG_SMB_MALLOC
printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
......
......@@ -41,7 +41,13 @@ smb_ioctl(struct inode *inode, struct file *filp,
case SMB_IOC_NEWCONN:
/* arg is smb_conn_opt, or NULL if no connection was made */
if (!arg) {
result = smb_wakeup(server);
result = 0;
smb_lock_server(server);
server->state = CONN_RETRIED;
printk(KERN_ERR "Connection attempt failed! [%d]\n",
server->conn_error);
smbiod_flush(server);
smb_unlock_server(server);
break;
}
......
......@@ -23,11 +23,14 @@
#include <linux/smbno.h>
#include <linux/smb_mount.h>
#include <net/sock.h>
#include <asm/string.h>
#include <asm/div64.h>
#include "smb_debug.h"
#include "proto.h"
#include "request.h"
/* Features. Undefine if they cause problems, this should perhaps be a
......@@ -40,8 +43,6 @@
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
#define SMB_CMD(packet) (*(packet+8))
#define SMB_WCT(packet) (*(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
......@@ -396,12 +397,13 @@ static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen,
}
/* encode_path for non-trans2 request SMBs */
static int smb_simple_encode_path(struct smb_sb_info *server, char **p,
static int smb_simple_encode_path(struct smb_request *req, char **p,
struct dentry * entry, struct qstr * name)
{
struct smb_sb_info *server = req->rq_server;
char *s = *p;
int res;
int maxlen = ((char *)server->packet + server->packet_size) - s;
int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s;
int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE);
if (!maxlen)
......@@ -413,8 +415,8 @@ static int smb_simple_encode_path(struct smb_sb_info *server, char **p,
* packet. If they are not they must be padded with 0.
*/
if (unicode) {
int align = s - (char *)server->packet;
if (align & 1) {
int align = s - (char *)req->rq_buffer;
if (!(align & 1)) {
*s++ = '\0';
maxlen--;
}
......@@ -560,7 +562,7 @@ smb_valid_packet(__u8 * packet)
&& packet[6] == 'M'
&& packet[7] == 'B'
&& (smb_len(packet) + 4 == SMB_HEADER_LEN
+ SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+ SMB_WCT(packet) * 2 + smb_bcc(packet)));
}
/* smb_verify: We check if we got the answer we expected, and if we
......@@ -573,7 +575,7 @@ smb_verify(__u8 * packet, int command, int wct, int bcc)
goto bad_command;
if (SMB_WCT(packet) < wct)
goto bad_wct;
if (bcc != -1 && SMB_BCC(packet) < bcc)
if (bcc != -1 && smb_bcc(packet) < bcc)
goto bad_bcc;
return 0;
......@@ -587,7 +589,7 @@ smb_verify(__u8 * packet, int command, int wct, int bcc)
goto fail;
bad_bcc:
printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n",
command, bcc, SMB_BCC(packet));
command, bcc, smb_bcc(packet));
fail:
return -EIO;
}
......@@ -615,8 +617,7 @@ smb_get_rsize(struct smb_sb_info *server)
int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2;
int size = smb_get_xmitsize(server, overhead);
VERBOSE("packet=%d, xmit=%d, size=%d\n",
server->packet_size, server->opt.max_xmit, size);
VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size);
return size;
}
......@@ -631,8 +632,7 @@ smb_get_wsize(struct smb_sb_info *server)
int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2;
int size = smb_get_xmitsize(server, overhead);
VERBOSE("packet=%d, xmit=%d, size=%d\n",
server->packet_size, server->opt.max_xmit, size);
VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size);
return size;
}
......@@ -641,14 +641,14 @@ smb_get_wsize(struct smb_sb_info *server)
* Convert SMB error codes to -E... errno values.
*/
int
smb_errno(struct smb_sb_info *server)
smb_errno(struct smb_request *req)
{
int errcls = server->rcls;
int error = server->err;
int errcls = req->rq_rcls;
int error = req->rq_err;
char *class = "Unknown";
VERBOSE("errcls %d code %d from command 0x%x\n",
errcls, error, SMB_CMD(server->packet));
errcls, error, SMB_CMD(req->rq_header));
if (errcls == ERRDOS) {
switch (error) {
......@@ -714,6 +714,7 @@ smb_errno(struct smb_sb_info *server)
case ERRbadpw:
return -EINVAL;
case ERRbadtype:
case ERRtimeout:
return -EIO;
case ERRaccess:
return -EACCES;
......@@ -759,138 +760,35 @@ smb_errno(struct smb_sb_info *server)
err_unknown:
printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n",
class, error, SMB_CMD(server->packet));
class, error, SMB_CMD(req->rq_header));
return -EIO;
}
/*
* 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.
* N.B. The server must be locked for this call.
*/
static int
smb_retry(struct smb_sb_info *server)
{
pid_t pid = server->conn_pid;
int error, result = 0;
if (server->state == CONN_VALID || server->state == CONN_RETRYING)
goto out;
smb_close_socket(server);
if (pid == 0) {
printk(KERN_ERR "smb_retry: no connection process\n");
server->state = CONN_RETRIED;
goto out;
}
/*
* Change state so that only one retry per server will be started.
*/
server->state = CONN_RETRYING;
/*
* Note: use the "priv" flag, as a user process may need to reconnect.
*/
error = kill_proc(pid, SIGUSR1, 1);
if (error) {
/* FIXME: this is fatal */
printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error);
goto out;
}
VERBOSE("signalled pid %d, waiting for new connection\n", pid);
/*
* Wait for the new connection.
*/
#ifdef SMB_RETRY_INTR
smb_unlock_server(server);
interruptible_sleep_on_timeout(&server->wait, 30*HZ);
smb_lock_server(server);
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 received 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!
*
* FIXME: go back to the interruptable version now that smbmount
* can avoid -EIO on the mountpoint when reconnecting?
*/
smb_unlock_server(server);
sleep_on_timeout(&server->wait, 30*HZ);
smb_lock_server(server);
#endif
/*
* Check for a valid connection.
*/
if (server->state == CONN_VALID) {
/* This should be changed to VERBOSE, except many smbfs
problems is with the userspace daemon not reconnecting. */
PARANOIA("successful, new pid=%d, generation=%d\n",
server->conn_pid, server->generation);
result = 1;
} else if (server->state == CONN_RETRYING) {
/* allow further attempts later */
server->state = CONN_RETRIED;
}
out:
return result;
}
/* 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_sb_info *s, int command, int wct, int bcc)
smb_request_ok(struct smb_request *req, int command, int wct, int bcc)
{
int result = -EIO;
s->rcls = 0;
s->err = 0;
int result;
/* Make sure we have a connection */
if (s->state != CONN_VALID) {
if (!smb_retry(s))
goto out;
}
req->rq_resp_wct = wct;
req->rq_resp_bcc = bcc;
if (smb_request(s) < 0) {
result = smb_add_request(req);
if (result != 0) {
DEBUG1("smb_request failed\n");
goto out;
}
if (smb_valid_packet(s->packet) != 0) {
if (smb_valid_packet(req->rq_header) != 0) {
PARANOIA("invalid packet!\n");
goto out;
}
/*
* Check for server errors.
*/
if (s->rcls != 0) {
result = smb_errno(s);
if (!result)
printk(KERN_DEBUG "smb_request_ok: rcls=%d, err=%d mapped to 0\n",
s->rcls, s->err);
/*
* Exit now even if the error was squashed ...
* packet verify will fail anyway.
*/
goto out;
}
result = smb_verify(s->packet, command, wct, bcc);
result = smb_verify(req->rq_header, command, wct, bcc);
out:
return result;
......@@ -904,6 +802,7 @@ int
smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
{
struct file *filp;
struct sock *sk;
int error;
VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid);
......@@ -931,12 +830,32 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
server->sock_file = filp;
server->conn_pid = current->pid;
smb_catch_keepalive(server);
server->opt = *opt;
server->generation += 1;
server->state = CONN_VALID;
error = 0;
if (server->conn_error) {
/*
* conn_error is the returncode we originally decided to
* drop the old connection on. This message should be positive
* and not make people ask questions on why smbfs is printing
* error messages ...
*/
printk(KERN_INFO "SMB connection re-established (%d)\n",
server->conn_error);
server->conn_error = 0;
}
/*
* Store the server in sock user_data (Only used by sunrpc)
*/
sk = SOCKET_I(filp->f_dentry->d_inode)->sk;
sk->user_data = server;
/* chain into the data_ready callback */
server->data_ready = xchg(&sk->data_ready, smb_data_ready);
/* check if we have an old smbmount that uses seconds for the
serverzone */
if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60)
......@@ -999,28 +918,14 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
server->opt.protocol, server->opt.max_xmit, server->conn_pid,
server->opt.capabilities);
/* Make sure we can fit a message of the negotiated size in our
packet buffer. */
if (server->opt.max_xmit > server->packet_size) {
int len = smb_round_length(server->opt.max_xmit);
char *buf = smb_vmalloc(len);
if (buf) {
if (server->packet)
smb_vfree(server->packet);
server->packet = buf;
server->packet_size = len;
} else {
/* else continue with the too small buffer? */
PARANOIA("Failed to allocate new packet buffer: "
"max_xmit=%d, packet_size=%d\n",
server->opt.max_xmit, server->packet_size);
server->opt.max_xmit = server->packet_size;
}
/* FIXME: this really should be done by smbmount. */
if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) {
server->opt.max_xmit = SMB_MAX_PACKET_SIZE;
}
out:
smb_unlock_server(server);
smb_wakeup(server);
smbiod_wake_up();
return error;
out_putf:
......@@ -1028,31 +933,15 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
goto out;
}
int
smb_wakeup(struct smb_sb_info *server)
{
#ifdef SMB_RETRY_INTR
wake_up_interruptible(&server->wait);
#else
wake_up(&server->wait);
#endif
return 0;
}
/* smb_setup_header: We completely set up the packet. You only have to
insert the command-specific fields */
__u8 *
smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc)
smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc)
{
__u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2;
__u8 *p = server->packet;
__u8 *buf = server->packet;
if (xmit_len > server->packet_size)
printk(KERN_DEBUG "smb_setup_header: "
"Aieee, xmit len > packet! len=%d, size=%d\n",
xmit_len, server->packet_size);
__u8 *p = req->rq_header;
struct smb_sb_info *server = req->rq_server;
p = smb_encode_smb_length(p, xmit_len - 4);
......@@ -1066,67 +955,81 @@ smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc
p += 19;
p += 8;
WSET(buf, smb_tid, server->opt.tid);
WSET(buf, smb_pid, 1);
WSET(buf, smb_uid, server->opt.server_uid);
WSET(buf, smb_mid, 1);
/* FIXME: the request will fail if the 'tid' is changed. This
should perhaps be set just before transmitting ... */
WSET(req->rq_header, smb_tid, server->opt.tid);
WSET(req->rq_header, smb_pid, 1);
WSET(req->rq_header, smb_uid, server->opt.server_uid);
if (server->opt.protocol > SMB_PROTOCOL_CORE) {
int flags = SMB_FLAGS_CASELESS_PATHNAMES;
int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS |
SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */
*(buf+smb_flg) = flags;
*(req->rq_header + smb_flg) = flags;
if (server->mnt->flags & SMB_MOUNT_UNICODE)
flags2 |= SMB_FLAGS2_UNICODE_STRINGS;
WSET(buf, smb_flg2, flags2);
WSET(req->rq_header, smb_flg2, flags2);
}
*p++ = wct; /* wct */
p += 2 * wct;
WSET(p, 0, bcc);
return p + 2;
/* Include the header in the data to send */
req->rq_iovlen = 1;
req->rq_iov[0].iov_base = req->rq_header;
req->rq_iov[0].iov_len = xmit_len - bcc;
return req->rq_buffer;
}
static void
smb_setup_bcc(struct smb_sb_info *server, __u8 * p)
smb_setup_bcc(struct smb_request *req, __u8 *p)
{
__u8 *packet = server->packet;
__u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
__u16 bcc = p - (pbcc + 2);
u16 bcc = p - req->rq_buffer;
u8 *pbcc = req->rq_header + SMB_HEADER_LEN + 2*SMB_WCT(req->rq_header);
WSET(pbcc, 0, bcc);
smb_encode_smb_length(packet,
SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
smb_encode_smb_length(req->rq_header, SMB_HEADER_LEN +
2*SMB_WCT(req->rq_header) - 2 + bcc);
/* Include the "bytes" in the data to send */
req->rq_iovlen = 2;
req->rq_iov[1].iov_base = req->rq_buffer;
req->rq_iov[1].iov_len = bcc;
}
/*
* Called with the server locked
*/
static int
smb_proc_seek(struct smb_sb_info *server, __u16 fileid,
__u16 mode, off_t offset)
{
int result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(server, SMBlseek, 4, 0);
WSET(server->packet, smb_vwv0, fileid);
WSET(server->packet, smb_vwv1, mode);
DSET(server->packet, smb_vwv2, offset);
smb_setup_header(req, SMBlseek, 4, 0);
WSET(req->rq_header, smb_vwv0, fileid);
WSET(req->rq_header, smb_vwv1, mode);
DSET(req->rq_header, smb_vwv2, offset);
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(server, SMBlseek, 2, 0);
result = smb_request_ok(req, SMBlseek, 2, 0);
if (result < 0) {
result = 0;
goto out;
goto out_free;
}
result = DVAL(server->packet, smb_vwv0);
result = DVAL(req->rq_header, smb_vwv0);
out_free:
smb_rput(req);
out:
return result;
}
/*
* We're called with the server locked, and we leave it that way.
*/
static int
smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
{
......@@ -1135,6 +1038,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
int mode, read_write = 0x42, read_only = 0x40;
int res;
char *p;
struct smb_request *req;
/*
* Attempt to open r/w, unless there are no write privileges.
......@@ -1153,20 +1057,21 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
mode = read_only;
#endif
res = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
retry:
p = smb_setup_header(server, SMBopen, 2, 0);
WSET(server->packet, smb_vwv0, mode);
WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
res = smb_simple_encode_path(server, &p, dentry, NULL);
p = smb_setup_header(req, SMBopen, 2, 0);
WSET(req->rq_header, smb_vwv0, mode);
WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
res = smb_simple_encode_path(req, &p, dentry, NULL);
if (res < 0)
goto out;
smb_setup_bcc(server, p);
goto out_free;
smb_setup_bcc(req, p);
res = smb_request_ok(server, SMBopen, 7, 0);
res = smb_request_ok(req, SMBopen, 7, 0);
if (res != 0) {
if (smb_retry(server))
goto retry;
if (mode == read_write &&
(res == -EACCES || res == -ETXTBSY || res == -EROFS))
{
......@@ -1175,17 +1080,19 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
mode = read_only;
goto retry;
}
goto out;
goto out_free;
}
/* We should now have data in vwv[0..6]. */
ei->fileid = WVAL(server->packet, smb_vwv0);
ei->attr = WVAL(server->packet, smb_vwv1);
ei->fileid = WVAL(req->rq_header, smb_vwv0);
ei->attr = WVAL(req->rq_header, smb_vwv1);
/* smb_vwv2 has mtime */
/* smb_vwv4 has size */
ei->access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK);
ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK);
ei->open = server->generation;
out_free:
smb_rput(req);
out:
return res;
}
......@@ -1210,11 +1117,9 @@ smb_open(struct dentry *dentry, int wish)
if (!smb_is_open(inode)) {
struct smb_sb_info *server = server_from_inode(inode);
smb_lock_server(server);
result = 0;
if (!smb_is_open(inode))
result = smb_proc_open(server, dentry, wish);
smb_unlock_server(server);
if (result) {
PARANOIA("%s/%s open failed, result=%d\n",
DENTRY_PATH(dentry), result);
......@@ -1240,20 +1145,27 @@ smb_open(struct dentry *dentry, int wish)
return result;
}
/* We're called with the server locked */
static int
smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime)
{
smb_setup_header(server, SMBclose, 3, 0);
WSET(server->packet, smb_vwv0, fileid);
DSET(server->packet, smb_vwv1, utc2local(server, mtime));
return smb_request_ok(server, SMBclose, 0, 0);
struct smb_request *req;
int result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(req, SMBclose, 3, 0);
WSET(req->rq_header, smb_vwv0, fileid);
DSET(req->rq_header, smb_vwv1, utc2local(server, mtime));
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(req, SMBclose, 0, 0);
smb_rput(req);
out:
return result;
}
/*
* Called with the server locked.
*
* Win NT 4.0 has an apparent bug in that it fails to update the
* modify time when writing to a file. As a workaround, we update
* both modify and access time locally, and post the times to the
......@@ -1312,9 +1224,7 @@ smb_close(struct inode *ino)
if (smb_is_open(ino)) {
struct smb_sb_info *server = server_from_inode(ino);
smb_lock_server(server);
result = smb_proc_close_inode(server, ino);
smb_unlock_server(server);
}
return result;
}
......@@ -1329,15 +1239,26 @@ smb_close_fileid(struct dentry *dentry, __u16 fileid)
struct smb_sb_info *server = server_from_dentry(dentry);
int result;
smb_lock_server(server);
result = smb_proc_close(server, fileid, CURRENT_TIME);
smb_unlock_server(server);
return result;
}
/* In smb_proc_read and smb_proc_write we do not retry, because the
file-id would not be valid after a reconnection. */
static void
smb_proc_read_data(struct smb_request *req)
{
req->rq_iov[0].iov_base = req->rq_buffer;
req->rq_iov[0].iov_len = 3;
req->rq_iov[1].iov_base = req->rq_page;
req->rq_iov[1].iov_len = req->rq_rsize;
req->rq_iovlen = 2;
req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;
}
static int
smb_proc_read(struct inode *inode, loff_t offset, int count, char *data)
{
......@@ -1345,33 +1266,32 @@ smb_proc_read(struct inode *inode, loff_t offset, int count, char *data)
__u16 returned_count, data_len;
unsigned char *buf;
int result;
struct smb_request *req;
u8 rbuf[4];
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
buf = server->packet;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(req, SMBread, 5, 0);
buf = req->rq_header;
WSET(buf, smb_vwv0, SMB_I(inode)->fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
result = smb_request_ok(server, SMBread, 5, -1);
if (result < 0)
goto out;
returned_count = WVAL(server->packet, smb_vwv0);
req->rq_page = data;
req->rq_rsize = count;
req->rq_callback = smb_proc_read_data;
req->rq_buffer = rbuf;
req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC;
buf = SMB_BUF(server->packet);
data_len = WVAL(buf, 1);
/* we can NOT simply trust the data_len given by the server ... */
if (data_len > server->packet_size - (buf+3 - server->packet)) {
printk(KERN_ERR "smb_proc_read: invalid data length!! "
"%d > %d - (%p - %p)\n",
data_len, server->packet_size, buf+3, server->packet);
result = -EIO;
goto out;
}
result = smb_request_ok(req, SMBread, 5, -1);
if (result < 0)
goto out_free;
returned_count = WVAL(req->rq_header, smb_vwv0);
memcpy(data, buf+3, data_len);
data_len = WVAL(rbuf, 1);
if (returned_count != data_len) {
printk(KERN_NOTICE "smb_proc_read: returned != data_len\n");
......@@ -1380,10 +1300,11 @@ smb_proc_read(struct inode *inode, loff_t offset, int count, char *data)
}
result = data_len;
out_free:
smb_rput(req);
out:
VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n",
inode->i_ino, SMB_I(inode)->fileid, count, result);
smb_unlock_server(server);
return result;
}
......@@ -1392,29 +1313,38 @@ smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data)
{
struct smb_sb_info *server = server_from_inode(inode);
int result;
__u8 *p;
__u16 fileid = SMB_I(inode)->fileid;
VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n",
inode->i_ino, SMB_I(inode)->fileid, count, offset,
server->packet_size);
u16 fileid = SMB_I(inode)->fileid;
u8 buf[4];
struct smb_request *req;
smb_lock_server(server);
p = smb_setup_header(server, SMBwrite, 5, count + 3);
WSET(server->packet, smb_vwv0, fileid);
WSET(server->packet, smb_vwv1, count);
DSET(server->packet, smb_vwv2, offset);
WSET(server->packet, smb_vwv4, 0);
*p++ = 1;
WSET(p, 0, count);
memcpy(p+2, data, count);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
result = smb_request_ok(server, SMBwrite, 1, 0);
VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n",
inode->i_ino, fileid, count, offset);
smb_setup_header(req, SMBwrite, 5, count + 3);
WSET(req->rq_header, smb_vwv0, fileid);
WSET(req->rq_header, smb_vwv1, count);
DSET(req->rq_header, smb_vwv2, offset);
WSET(req->rq_header, smb_vwv4, 0);
buf[0] = 1;
WSET(buf, 1, count); /* yes, again ... */
req->rq_iov[1].iov_base = buf;
req->rq_iov[1].iov_len = 3;
req->rq_iov[2].iov_base = (char *) data;
req->rq_iov[2].iov_len = count;
req->rq_iovlen = 3;
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(req, SMBwrite, 1, 0);
if (result >= 0)
result = WVAL(server->packet, smb_vwv0);
result = WVAL(req->rq_header, smb_vwv0);
smb_unlock_server(server);
smb_rput(req);
out:
return result;
}
......@@ -1422,17 +1352,51 @@ smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data)
* In smb_proc_readX and smb_proc_writeX we do not retry, because the
* file-id would not be valid after a reconnection.
*/
#define SMB_READX_MAX_PAD 64
static void
smb_proc_readX_data(struct smb_request *req)
{
/* header length, excluding the netbios length (-4) */
int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2;
int data_off = WVAL(req->rq_header, smb_vwv6);
/*
* Some genius made the padding to the data bytes arbitrary.
* So we must first calculate the amount of padding used by the server.
*/
data_off -= hdrlen;
if (data_off > SMB_READX_MAX_PAD) {
PARANOIA("offset is larger than max pad!\n");
PARANOIA("%d > %d\n", data_off, SMB_READX_MAX_PAD);
req->rq_rlen = req->rq_bufsize + 1;
return;
}
req->rq_iov[0].iov_base = req->rq_buffer;
req->rq_iov[0].iov_len = data_off;
req->rq_iov[1].iov_base = req->rq_page;
req->rq_iov[1].iov_len = req->rq_rsize;
req->rq_iovlen = 2;
req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;
}
static int
smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data)
{
struct smb_sb_info *server = server_from_inode(inode);
u16 data_len, data_off;
unsigned char *buf;
int result;
struct smb_request *req;
static char pad[SMB_READX_MAX_PAD];
smb_lock_server(server);
smb_setup_header(server, SMBreadX, 12, 0);
buf = server->packet;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(req, SMBreadX, 12, 0);
buf = req->rq_header;
WSET(buf, smb_vwv0, 0x00ff);
WSET(buf, smb_vwv1, 0);
WSET(buf, smb_vwv2, SMB_I(inode)->fileid);
......@@ -1444,27 +1408,21 @@ smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data)
DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */
WSET(buf, smb_vwv11, 0);
result = smb_request_ok(server, SMBreadX, 12, -1);
if (result < 0)
goto out;
data_len = WVAL(server->packet, smb_vwv5);
data_off = WVAL(server->packet, smb_vwv6);
buf = smb_base(server->packet) + data_off;
/* we can NOT simply trust the info given by the server ... */
if (data_len > server->packet_size - (buf - server->packet)) {
printk(KERN_ERR "smb_proc_read: invalid data length!! "
"%d > %d - (%p - %p)\n",
data_len, server->packet_size, buf, server->packet);
result = -EIO;
goto out;
}
req->rq_page = data;
req->rq_rsize = count;
req->rq_callback = smb_proc_readX_data;
req->rq_buffer = pad;
req->rq_bufsize = SMB_READX_MAX_PAD;
req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY;
memcpy(data, buf, data_len);
result = data_len;
result = smb_request_ok(req, SMBreadX, 12, -1);
if (result < 0)
goto out_free;
result = WVAL(req->rq_header, smb_vwv5);
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n",
inode->i_ino, SMB_I(inode)->fileid, count, result);
return result;
......@@ -1476,33 +1434,42 @@ smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data)
struct smb_sb_info *server = server_from_inode(inode);
int result;
u8 *p;
static u8 pad[4];
struct smb_request *req;
VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n",
inode->i_ino, SMB_I(inode)->fileid, count, offset,
server->packet_size);
smb_lock_server(server);
p = smb_setup_header(server, SMBwriteX, 14, count + 2);
WSET(server->packet, smb_vwv0, 0x00ff);
WSET(server->packet, smb_vwv1, 0);
WSET(server->packet, smb_vwv2, SMB_I(inode)->fileid);
DSET(server->packet, smb_vwv3, (u32)offset); /* low 32 bits */
DSET(server->packet, smb_vwv5, 0);
WSET(server->packet, smb_vwv7, 0); /* write mode */
WSET(server->packet, smb_vwv8, 0);
WSET(server->packet, smb_vwv9, 0);
WSET(server->packet, smb_vwv10, count); /* data length */
WSET(server->packet, smb_vwv11, p + 2 - smb_base(server->packet));
DSET(server->packet, smb_vwv12, (u32)(offset >> 32));
*p++ = 0; /* FIXME: pad to short or long ... change +2 above also */
*p++ = 0;
memcpy(p, data, count);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
result = smb_request_ok(server, SMBwriteX, 6, 0);
VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n",
inode->i_ino, SMB_I(inode)->fileid, count, offset);
p = smb_setup_header(req, SMBwriteX, 14, count + 1);
WSET(req->rq_header, smb_vwv0, 0x00ff);
WSET(req->rq_header, smb_vwv1, 0);
WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid);
DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */
DSET(req->rq_header, smb_vwv5, 0);
WSET(req->rq_header, smb_vwv7, 0); /* write mode */
WSET(req->rq_header, smb_vwv8, 0);
WSET(req->rq_header, smb_vwv9, 0);
WSET(req->rq_header, smb_vwv10, count); /* data length */
WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1);
DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32));
req->rq_iov[1].iov_base = pad;
req->rq_iov[1].iov_len = 1;
req->rq_iov[2].iov_base = (char *) data;
req->rq_iov[2].iov_len = count;
req->rq_iovlen = 3;
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(req, SMBwriteX, 6, 0);
if (result >= 0)
result = WVAL(server->packet, smb_vwv2);
result = WVAL(req->rq_header, smb_vwv2);
smb_unlock_server(server);
smb_rput(req);
out:
return result;
}
......@@ -1512,29 +1479,30 @@ 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 result;
struct smb_request *req;
smb_lock_server(server);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
retry:
p = smb_setup_header(server, SMBcreate, 3, 0);
WSET(server->packet, smb_vwv0, attr);
DSET(server->packet, smb_vwv1, utc2local(server, ctime));
result = smb_simple_encode_path(server, &p, dentry, NULL);
p = smb_setup_header(req, SMBcreate, 3, 0);
WSET(req->rq_header, smb_vwv0, attr);
DSET(req->rq_header, smb_vwv1, utc2local(server, ctime));
result = smb_simple_encode_path(req, &p, dentry, NULL);
if (result < 0)
goto out;
smb_setup_bcc(server, p);
goto out_free;
smb_setup_bcc(req, p);
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);
result = smb_request_ok(req, SMBcreate, 1, 0);
if (result < 0)
goto out_free;
*fileid = WVAL(req->rq_header, smb_vwv0);
result = 0;
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
......@@ -1544,28 +1512,29 @@ smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry)
struct smb_sb_info *server = server_from_dentry(old_dentry);
char *p;
int result;
struct smb_request *req;
smb_lock_server(server);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
retry:
p = smb_setup_header(server, SMBmv, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
result = smb_simple_encode_path(server, &p, old_dentry, NULL);
p = smb_setup_header(req, SMBmv, 1, 0);
WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
result = smb_simple_encode_path(req, &p, old_dentry, NULL);
if (result < 0)
goto out;
result = smb_simple_encode_path(server, &p, new_dentry, NULL);
goto out_free;
result = smb_simple_encode_path(req, &p, new_dentry, NULL);
if (result < 0)
goto out;
smb_setup_bcc(server, p);
goto out_free;
smb_setup_bcc(req, p);
if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0)
goto out_free;
result = 0;
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
......@@ -1578,25 +1547,26 @@ smb_proc_generic_command(struct dentry *dentry, __u8 command)
struct smb_sb_info *server = server_from_dentry(dentry);
char *p;
int result;
struct smb_request *req;
smb_lock_server(server);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
retry:
p = smb_setup_header(server, command, 0, 0);
result = smb_simple_encode_path(server, &p, dentry, NULL);
p = smb_setup_header(req, command, 0, 0);
result = smb_simple_encode_path(req, &p, dentry, NULL);
if (result < 0)
goto out;
smb_setup_bcc(server, p);
goto out_free;
smb_setup_bcc(req, p);
result = smb_request_ok(server, command, 0, 0);
if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
result = smb_request_ok(req, command, 0, 0);
if (result < 0)
goto out_free;
result = 0;
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
......@@ -1616,7 +1586,6 @@ smb_proc_rmdir(struct dentry *dentry)
/*
* Removes readonly attribute from a file. Used by unlink to give posix
* semantics.
* Note: called with the server locked.
*/
static int
smb_set_rw(struct dentry *dentry,struct smb_sb_info *server)
......@@ -1647,18 +1616,21 @@ smb_proc_unlink(struct dentry *dentry)
int flag = 0;
char *p;
int result;
struct smb_request *req;
smb_lock_server(server);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
retry:
p = smb_setup_header(server, SMBunlink, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
result = smb_simple_encode_path(server, &p, dentry, NULL);
p = smb_setup_header(req, SMBunlink, 1, 0);
WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN);
result = smb_simple_encode_path(req, &p, dentry, NULL);
if (result < 0)
goto out;
smb_setup_bcc(server, p);
goto out_free;
smb_setup_bcc(req, p);
if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
if ((result = smb_request_ok(req, SMBunlink, 0, 0)) < 0) {
#if SMBFS_POSIX_UNLINK
if (result == -EACCES && !flag) {
/* Posix semantics is for the read-only state
......@@ -1681,25 +1653,34 @@ smb_proc_unlink(struct dentry *dentry)
}
}
#endif
if (smb_retry(server))
goto retry;
goto out;
goto out_free;
}
result = 0;
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
/*
* Called with the server locked
*/
int
smb_proc_flush(struct smb_sb_info *server, __u16 fileid)
{
smb_setup_header(server, SMBflush, 1, 0);
WSET(server->packet, smb_vwv0, fileid);
return smb_request_ok(server, SMBflush, 0, 0);
int result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(req, SMBflush, 1, 0);
WSET(req->rq_header, smb_vwv0, fileid);
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(req, SMBflush, 0, 0);
smb_rput(req);
out:
return result;
}
static int
......@@ -1717,39 +1698,41 @@ static int
smb_proc_trunc64(struct inode *inode, loff_t length)
{
struct smb_sb_info *server = server_from_inode(inode);
int command;
int result;
char *param = server->temp_buf;
char data[8];
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
char *param;
char *data;
struct smb_request *req;
smb_lock_server(server);
command = TRANSACT2_SETFILEINFO;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 14)))
goto out;
param = req->rq_buffer;
data = req->rq_buffer + 6;
/* FIXME: must we also set allocation size? winNT seems to do that */
retry:
WSET(param, 0, SMB_I(inode)->fileid);
WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO);
WSET(param, 4, 0);
LSET(data, 0, length);
result = smb_trans2_request(server, command,
8, data, 6, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
req->rq_trans2_command = TRANSACT2_SETFILEINFO;
req->rq_ldata = 8;
req->rq_data = data;
req->rq_lparm = 6;
req->rq_parm = param;
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_add_request(req);
if (result < 0)
goto out_free;
result = 0;
if (server->rcls != 0)
result = smb_errno(server);
if (req->rq_rcls != 0)
result = smb_errno(req);
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
......@@ -1766,9 +1749,7 @@ smb_proc_trunc95(struct inode *inode, loff_t length)
*
* FIXME: is this still necessary?
*/
smb_lock_server(server);
smb_proc_flush(server, SMB_I(inode)->fileid);
smb_unlock_server(server);
return result;
}
......@@ -1824,7 +1805,8 @@ smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
*/
static char *
smb_decode_short_dirent(struct smb_sb_info *server, char *p,
struct qstr *qname, struct smb_fattr *fattr)
struct qstr *qname, struct smb_fattr *fattr,
unsigned char *name_buf)
{
int len;
......@@ -1867,12 +1849,12 @@ smb_decode_short_dirent(struct smb_sb_info *server, char *p,
#endif
qname->len = 0;
len = server->ops->convert(server->name_buf, SMB_MAXNAMELEN,
len = server->ops->convert(name_buf, SMB_MAXNAMELEN,
qname->name, len,
server->remote_nls, server->local_nls);
if (len > 0) {
qname->len = len;
qname->name = server->name_buf;
qname->name = name_buf;
DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name);
}
......@@ -1904,28 +1886,36 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
len: 3,
};
unsigned char *last_status;
struct smb_request *req;
unsigned char *name_buf;
VERBOSE("%s/%s\n", DENTRY_PATH(dir));
lock_kernel();
smb_lock_server(server);
result = -ENOMEM;
if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL)))
goto out;
first = 1;
entries = 0;
entries_seen = 2; /* implicit . and .. */
result = -ENOMEM;
if (! (req = smb_alloc_request(server, server->opt.max_xmit)))
goto out_name;
while (1) {
p = smb_setup_header(server, SMBsearch, 2, 0);
WSET(server->packet, smb_vwv0, entries_asked);
WSET(server->packet, smb_vwv1, aDIR);
p = smb_setup_header(req, SMBsearch, 2, 0);
WSET(req->rq_header, smb_vwv0, entries_asked);
WSET(req->rq_header, smb_vwv1, aDIR);
if (first == 1) {
result = smb_simple_encode_path(server, &p, dir, &mask);
result = smb_simple_encode_path(req, &p, dir, &mask);
if (result < 0)
goto unlock_return;
if (p + 3 > (char*)server->packet+server->packet_size) {
goto out_free;
if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) {
result = -ENAMETOOLONG;
goto unlock_return;
goto out_free;
}
*p++ = 5;
WSET(p, 0, 0);
......@@ -1933,9 +1923,9 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
first = 0;
} else {
if (p + 5 + SMB_STATUS_SIZE >
(char*)server->packet + server->packet_size) {
(char *)req->rq_buffer + req->rq_bufsize) {
result = -ENAMETOOLONG;
goto unlock_return;
goto out_free;
}
*p++ = 4;
......@@ -1947,44 +1937,38 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
p += SMB_STATUS_SIZE;
}
smb_setup_bcc(server, p);
smb_setup_bcc(req, p);
result = smb_request_ok(server, SMBsearch, 1, -1);
result = smb_request_ok(req, SMBsearch, 1, -1);
if (result < 0) {
if ((server->rcls == ERRDOS) &&
(server->err == ERRnofiles))
if ((req->rq_rcls == ERRDOS) &&
(req->rq_err == ERRnofiles))
break;
if (smb_retry(server)) {
ctl->idx = -1; /* retry */
result = 0;
}
goto unlock_return;
goto out_free;
}
p = SMB_VWV(server->packet);
count = WVAL(p, 0);
count = WVAL(req->rq_header, smb_vwv0);
if (count <= 0)
break;
result = -EIO;
bcc = WVAL(p, 2);
bcc = smb_bcc(req->rq_header);
if (bcc != count * SMB_DIRINFO_SIZE + 3)
goto unlock_return;
p += 7;
goto out_free;
p = req->rq_buffer + 3;
/* Make sure the response fits in the buffer. Fixed sized
entries means we don't have to check in the decode loop. */
last_status = SMB_BUF(server->packet) + 3 + (count - 1) *
SMB_DIRINFO_SIZE;
last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE;
if (last_status + SMB_DIRINFO_SIZE >=
server->packet + server->packet_size) {
req->rq_buffer + req->rq_bufsize) {
printk(KERN_ERR "smb_proc_readdir_short: "
"last dir entry outside buffer! "
"%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status,
server->packet_size, server->packet);
goto unlock_return;
req->rq_bufsize, req->rq_buffer);
goto out_free;
}
/* Read the last entry into the status field. */
......@@ -1995,7 +1979,7 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
for (i = 0; i < count; i++) {
p = smb_decode_short_dirent(server, p,
&qname, &fattr);
&qname, &fattr, name_buf);
if (qname.len == 0)
continue;
......@@ -2013,8 +1997,11 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
}
result = entries;
unlock_return:
smb_unlock_server(server);
out_free:
smb_rput(req);
out_name:
kfree(name_buf);
out:
unlock_kernel();
return result;
}
......@@ -2032,7 +2019,8 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
*/
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
struct qstr *qname, struct smb_fattr *fattr)
struct qstr *qname, struct smb_fattr *fattr,
unsigned char *name_buf)
{
char *result;
unsigned int len = 0;
......@@ -2114,12 +2102,12 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
#endif
qname->len = 0;
n = server->ops->convert(server->name_buf, SMB_MAXNAMELEN,
n = server->ops->convert(name_buf, SMB_MAXNAMELEN,
qname->name, len,
server->remote_nls, server->local_nls);
if (n > 0) {
qname->len = n;
qname->name = server->name_buf;
qname->name = name_buf;
}
out:
......@@ -2153,7 +2141,7 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
struct smb_fattr fattr;
unsigned char *p, *lastname;
char *mask, *param = server->temp_buf;
char *mask, *param;
__u16 command;
int first, entries_seen;
......@@ -2161,16 +2149,15 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
int info_level = 260;
const int max_matches = 512;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
int ff_searchcount = 0;
int ff_eos = 0;
int ff_lastname = 0;
int ff_dir_handle = 0;
int loop_count = 0;
int mask_len, i, result;
unsigned int ff_searchcount = 0;
unsigned int ff_eos = 0;
unsigned int ff_lastname = 0;
unsigned int ff_dir_handle = 0;
unsigned int loop_count = 0;
unsigned int mask_len, i;
int result;
struct smb_request *req;
unsigned char *name_buf;
static struct qstr star = {
name: "*",
len: 1,
......@@ -2184,7 +2171,12 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
if (server->opt.protocol < SMB_PROTOCOL_NT1)
info_level = 1;
smb_lock_server(server);
result = -ENOMEM;
if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL)))
goto out;
if (! (req = smb_alloc_request(server, server->opt.max_xmit)))
goto out_name;
param = req->rq_buffer;
/*
* Encode the initial path
......@@ -2194,7 +2186,7 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star);
if (mask_len < 0) {
result = mask_len;
goto unlock_return;
goto out_free;
}
mask_len--; /* mask_len is strlen, not #bytes */
first = 1;
......@@ -2223,8 +2215,8 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
} else {
command = TRANSACT2_FINDNEXT;
VERBOSE("handle=0x%X, lastname=%d, mask=%s\n",
ff_dir_handle, ff_lastname, mask);
VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n",
ff_dir_handle, ff_lastname, mask_len, mask);
WSET(param, 0, ff_dir_handle); /* search handle */
WSET(param, 2, max_matches); /* max count */
......@@ -2233,23 +2225,19 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END);
}
result = smb_trans2_request(server, command,
0, NULL, 12 + mask_len + 1, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
req->rq_trans2_command = command;
req->rq_ldata = 0;
req->rq_data = NULL;
req->rq_lparm = 12 + mask_len + 1;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0) {
if (smb_retry(server)) {
PARANOIA("error=%d, retrying\n", result);
ctl->idx = -1; /* retry */
result = 0;
goto unlock_return;
}
PARANOIA("error=%d, breaking\n", result);
break;
}
if (server->rcls == ERRSRV && server->err == ERRerror) {
if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) {
/* a damn Win95 bug - sometimes it clags if you
ask it too fast */
current->state = TASK_INTERRUPTIBLE;
......@@ -2257,8 +2245,8 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
continue;
}
if (server->rcls != 0) {
result = smb_errno(server);
if (req->rq_rcls != 0) {
result = smb_errno(req);
PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n",
mask, result, server->rcls, server->err);
break;
......@@ -2266,19 +2254,53 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
/* 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);
ff_dir_handle = WVAL(req->rq_parm, 0);
ff_searchcount = WVAL(req->rq_parm, 2);
ff_eos = WVAL(req->rq_parm, 4);
ff_lastname = WVAL(req->rq_parm, 8);
} else {
ff_searchcount = WVAL(resp_param, 0);
ff_eos = WVAL(resp_param, 2);
ff_lastname = WVAL(resp_param, 6);
ff_searchcount = WVAL(req->rq_parm, 0);
ff_eos = WVAL(req->rq_parm, 2);
ff_lastname = WVAL(req->rq_parm, 6);
}
if (ff_searchcount == 0)
break;
/* Now we are ready to parse smb directory entries. */
/* point to the data bytes */
p = req->rq_data;
for (i = 0; i < ff_searchcount; i++) {
/* make sure we stay within the buffer */
if (p >= req->rq_data + req->rq_ldata) {
printk(KERN_ERR "smb_proc_readdir_long: "
"dirent pointer outside buffer! "
"%p %d@%p\n",
p, req->rq_ldata, req->rq_data);
result = -EIO; /* always a comm. error? */
goto out_free;
}
p = smb_decode_long_dirent(server, p, info_level,
&qname, &fattr, name_buf);
/* ignore . and .. from the server */
if (entries_seen == 2 && qname.name[0] == '.') {
if (qname.len == 1)
continue;
if (qname.name[1] == '.' && qname.len == 2)
continue;
}
if (!smb_fill_cache(filp, dirent, filldir, ctl,
&qname, &fattr))
; /* stop reading? */
entries_seen++;
}
VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos);
/*
* We might need the lastname for continuations.
*
......@@ -2293,18 +2315,18 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
* Both are happy if we return the data they point to. So we do.
*/
mask_len = 0;
if (ff_lastname > 0 && ff_lastname < resp_data_len) {
lastname = resp_data + ff_lastname;
if (ff_lastname > 0 && ff_lastname < req->rq_ldata) {
lastname = req->rq_data + ff_lastname;
switch (info_level) {
case 260:
mask_len = resp_data_len - ff_lastname;
mask_len = req->rq_ldata - ff_lastname;
break;
case 1:
/* lastname points to a length byte */
mask_len = *lastname++;
if (ff_lastname + 1 + mask_len > resp_data_len)
mask_len = resp_data_len - ff_lastname - 1;
if (ff_lastname + 1 + mask_len > req->rq_ldata)
mask_len = req->rq_ldata - ff_lastname - 1;
break;
}
......@@ -2320,49 +2342,17 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
}
mask_len = strnlen(mask, mask_len);
VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n",
mask_len, ff_lastname, resp_data_len, mask_len, mask);
/* Now we are ready to parse smb directory entries. */
/* point to the data bytes */
p = resp_data;
for (i = 0; i < ff_searchcount; i++) {
/* make sure we stay within the buffer */
if (p >= resp_data + resp_data_len) {
printk(KERN_ERR "smb_proc_readdir_long: "
"dirent pointer outside buffer! "
"%p %d@%p %d@%p\n",
p, resp_data_len, resp_data,
server->packet_size, server->packet);
result = -EIO; /* always a comm. error? */
goto unlock_return;
}
p = smb_decode_long_dirent(server, p, info_level,
&qname, &fattr);
/* ignore . and .. from the server */
if (entries_seen == 2 && qname.name[0] == '.') {
if (qname.len == 1)
continue;
if (qname.name[1] == '.' && qname.len == 2)
continue;
}
if (!smb_fill_cache(filp, dirent, filldir, ctl,
&qname, &fattr))
; /* stop reading? */
entries_seen++;
}
VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos);
mask_len, ff_lastname, req->rq_ldata, mask_len, mask);
first = 0;
loop_count = 0;
}
unlock_return:
smb_unlock_server(server);
out_free:
smb_rput(req);
out_name:
kfree(name_buf);
out:
unlock_kernel();
return result;
}
......@@ -2370,7 +2360,6 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
/*
* This version uses the trans2 TRANSACT2_FINDFIRST message
* to get the attribute data.
* Note: called with the server locked.
*
* Bugs Noted:
*/
......@@ -2378,19 +2367,21 @@ static int
smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
struct smb_fattr *fattr)
{
char *param = server->temp_buf, *mask = param + 12;
char *param, *mask;
__u16 date, time;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
int mask_len, result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
mask = param + 12;
retry:
mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL);
if (mask_len < 0) {
result = mask_len;
goto out;
goto out_free;
}
VERBOSE("name=%s, len=%d\n", mask, mask_len);
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
......@@ -2399,86 +2390,82 @@ smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
WSET(param, 6, 1); /* info_level */
DSET(param, 8, 0);
result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
0, NULL, 12 + mask_len, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
req->rq_trans2_command = TRANSACT2_FINDFIRST;
req->rq_ldata = 0;
req->rq_data = NULL;
req->rq_lparm = 12 + mask_len;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
if (server->rcls != 0)
{
result = smb_errno(server);
goto out_free;
if (server->rcls != 0) {
result = smb_errno(req);
#ifdef SMBFS_PARANOIA
if (result != -ENOENT)
PARANOIA("error for %s, rcls=%d, err=%d\n",
mask, server->rcls, server->err);
mask, req->rq_rcls, req->rq_err);
#endif
goto out;
goto out_free;
}
/* Make sure we got enough data ... */
result = -EINVAL;
if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
{
if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) {
PARANOIA("bad result for %s, len=%d, count=%d\n",
mask, resp_data_len, WVAL(resp_param, 2));
goto out;
mask, req->rq_ldata, WVAL(req->rq_parm, 2));
goto out_free;
}
/*
* Decode the response into the fattr ...
*/
date = WVAL(resp_data, 0);
time = WVAL(resp_data, 2);
date = WVAL(req->rq_data, 0);
time = WVAL(req->rq_data, 2);
fattr->f_ctime = date_dos2unix(server, date, time);
date = WVAL(resp_data, 4);
time = WVAL(resp_data, 6);
date = WVAL(req->rq_data, 4);
time = WVAL(req->rq_data, 6);
fattr->f_atime = date_dos2unix(server, date, time);
date = WVAL(resp_data, 8);
time = WVAL(resp_data, 10);
date = WVAL(req->rq_data, 8);
time = WVAL(req->rq_data, 10);
fattr->f_mtime = date_dos2unix(server, date, time);
VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n",
mask, date, time, fattr->f_mtime);
fattr->f_size = DVAL(resp_data, 12);
fattr->f_size = DVAL(req->rq_data, 12);
/* ULONG allocation size */
fattr->attr = WVAL(resp_data, 20);
fattr->attr = WVAL(req->rq_data, 20);
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
/*
* Note: called with the server locked.
*/
static int
smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *fattr)
{
int result;
char *p;
struct smb_request *req;
retry:
p = smb_setup_header(server, SMBgetatr, 0, 0);
result = smb_simple_encode_path(server, &p, dir, NULL);
if (result < 0)
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
smb_setup_bcc(server, p);
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
fattr->attr = WVAL(server->packet, smb_vwv0);
fattr->f_mtime = local2utc(server, DVAL(server->packet, smb_vwv1));
fattr->f_size = DVAL(server->packet, smb_vwv3);
p = smb_setup_header(req, SMBgetatr, 0, 0);
result = smb_simple_encode_path(req, &p, dir, NULL);
if (result < 0)
goto out_free;
smb_setup_bcc(req, p);
if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0)
goto out_free;
fattr->attr = WVAL(req->rq_header, smb_vwv0);
fattr->f_mtime = local2utc(server, DVAL(req->rq_header, smb_vwv1));
fattr->f_size = DVAL(req->rq_header, smb_vwv3);
fattr->f_ctime = fattr->f_mtime;
fattr->f_atime = fattr->f_mtime;
#ifdef SMBFS_DEBUG_TIMESTAMP
......@@ -2487,26 +2474,24 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
#endif
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
/*
* Note: called with the server locked.
*
* Bugs Noted:
* (1) Win 95 swaps the date and time fields in the standard info level.
*/
static int
smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
int *lrdata, unsigned char **rdata,
int *lrparam, unsigned char **rparam,
int infolevel)
struct smb_request *req, int infolevel)
{
char *p, *param = server->temp_buf;
char *p, *param;
int result;
retry:
param = req->rq_buffer;
WSET(param, 0, infolevel);
DSET(param, 2, 0);
result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
......@@ -2514,28 +2499,25 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
goto out;
p = param + 6 + result;
result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
0, NULL, p - param, param,
lrdata, rdata,
lrparam, rparam);
req->rq_trans2_command = TRANSACT2_QPATHINFO;
req->rq_ldata = 0;
req->rq_data = NULL;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
if (server->rcls != 0)
{
if (server->rcls != 0) {
VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
&param[6], result, server->rcls, server->err);
result = smb_errno(server);
&param[6], result, req->rq_rcls, req->rq_err);
result = smb_errno(req);
goto out;
}
result = -ENOENT;
if (*lrdata < 22)
{
if (req->rq_ldata < 22) {
PARANOIA("not enough data for %s, len=%d\n",
&param[6], *lrdata);
&param[6], req->rq_ldata);
goto out;
}
......@@ -2550,18 +2532,17 @@ smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir,
{
u16 date, time;
int off_date = 0, off_time = 2;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
int result = smb_proc_getattr_trans2(server, dir,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param,
SMB_INFO_STANDARD);
if (result < 0)
int result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
result = smb_proc_getattr_trans2(server, dir, req, SMB_INFO_STANDARD);
if (result < 0)
goto out_free;
/*
* Kludge alert: Win 95 swaps the date and time field,
* contrary to the CIFS docs and Win NT practice.
......@@ -2570,24 +2551,26 @@ smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir,
off_date = 2;
off_time = 0;
}
date = WVAL(resp_data, off_date);
time = WVAL(resp_data, off_time);
date = WVAL(req->rq_data, off_date);
time = WVAL(req->rq_data, off_time);
attr->f_ctime = date_dos2unix(server, date, time);
date = WVAL(resp_data, 4 + off_date);
time = WVAL(resp_data, 4 + off_time);
date = WVAL(req->rq_data, 4 + off_date);
time = WVAL(req->rq_data, 4 + off_time);
attr->f_atime = date_dos2unix(server, date, time);
date = WVAL(resp_data, 8 + off_date);
time = WVAL(resp_data, 8 + off_time);
date = WVAL(req->rq_data, 8 + off_date);
time = WVAL(req->rq_data, 8 + off_time);
attr->f_mtime = date_dos2unix(server, date, time);
#ifdef SMBFS_DEBUG_TIMESTAMP
printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
DENTRY_PATH(dir), date, time, attr->f_mtime);
#endif
attr->f_size = DVAL(resp_data, 12);
attr->attr = WVAL(resp_data, 20);
attr->f_size = DVAL(req->rq_data, 12);
attr->attr = WVAL(req->rq_data, 20);
out_free:
smb_rput(req);
out:
return result;
}
......@@ -2596,27 +2579,29 @@ static int
smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *attr)
{
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
struct smb_request *req;
int result;
int result = smb_proc_getattr_trans2(server, dir,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param,
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
result = smb_proc_getattr_trans2(server, dir, req,
SMB_QUERY_FILE_ALL_INFO);
if (result < 0)
goto out;
goto out_free;
attr->f_ctime = smb_ntutc2unixutc(LVAL(resp_data, 0));
attr->f_atime = smb_ntutc2unixutc(LVAL(resp_data, 8));
attr->f_mtime = smb_ntutc2unixutc(LVAL(resp_data, 16));
attr->f_ctime = smb_ntutc2unixutc(LVAL(req->rq_data, 0));
attr->f_atime = smb_ntutc2unixutc(LVAL(req->rq_data, 8));
attr->f_mtime = smb_ntutc2unixutc(LVAL(req->rq_data, 16));
/* change (24) */
attr->attr = WVAL(resp_data, 32);
attr->attr = WVAL(req->rq_data, 32);
/* pad? (34) */
/* allocated size (40) */
attr->f_size = LVAL(resp_data, 48);
attr->f_size = LVAL(req->rq_data, 48);
out_free:
smb_rput(req);
out:
return result;
}
......@@ -2656,21 +2641,17 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
struct smb_sb_info *server = server_from_dentry(dir);
int result;
smb_lock_server(server);
smb_init_dirent(server, fattr);
result = server->ops->getattr(server, dir, fattr);
smb_finish_dirent(server, fattr);
smb_unlock_server(server);
return result;
}
/*
* Called with the server locked. Because of bugs in the
* core protocol, we use this only to set attributes. See
* smb_proc_settime() below for timestamp handling.
* Because of bugs in the core protocol, we use this only to set
* attributes. See smb_proc_settime() below for timestamp handling.
*
* Bugs Noted:
* (1) If mtime is non-zero, both Win 3.1 and Win 95 fail
......@@ -2685,34 +2666,38 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
{
char *p;
int result;
struct smb_request *req;
retry:
p = smb_setup_header(server, SMBsetatr, 8, 0);
WSET(server->packet, smb_vwv0, attr);
DSET(server->packet, smb_vwv1, 0); /* mtime */
WSET(server->packet, smb_vwv3, 0); /* reserved values */
WSET(server->packet, smb_vwv4, 0);
WSET(server->packet, smb_vwv5, 0);
WSET(server->packet, smb_vwv6, 0);
WSET(server->packet, smb_vwv7, 0);
result = smb_simple_encode_path(server, &p, dentry, NULL);
if (result < 0)
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
if (p + 2 > (char *)server->packet + server->packet_size) {
p = smb_setup_header(req, SMBsetatr, 8, 0);
WSET(req->rq_header, smb_vwv0, attr);
DSET(req->rq_header, smb_vwv1, 0); /* mtime */
WSET(req->rq_header, smb_vwv3, 0); /* reserved values */
WSET(req->rq_header, smb_vwv4, 0);
WSET(req->rq_header, smb_vwv5, 0);
WSET(req->rq_header, smb_vwv6, 0);
WSET(req->rq_header, smb_vwv7, 0);
result = smb_simple_encode_path(req, &p, dentry, NULL);
if (result < 0)
goto out_free;
if (p + 2 > (char *)req->rq_buffer + req->rq_bufsize) {
result = -ENAMETOOLONG;
goto out;
goto out_free;
}
*p++ = 4;
*p++ = 0;
smb_setup_bcc(server, p);
smb_setup_bcc(req, p);
result = smb_request_ok(server, SMBsetatr, 0, 0);
if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
result = smb_request_ok(req, SMBsetatr, 0, 0);
if (result < 0)
goto out_free;
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
......@@ -2730,15 +2715,12 @@ smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr)
VERBOSE("setting %s/%s, open=%d\n",
DENTRY_PATH(dir), smb_is_open(dir->d_inode));
smb_lock_server(server);
result = smb_proc_setattr_core(server, dir, fattr->attr);
smb_unlock_server(server);
return result;
}
/*
* Called with the server locked. Sets the timestamps for an
* file open with write permissions.
* Sets the timestamps for an file open with write permissions.
*/
static int
smb_proc_setattr_ext(struct smb_sb_info *server,
......@@ -2746,38 +2728,40 @@ smb_proc_setattr_ext(struct smb_sb_info *server,
{
__u16 date, time;
int result;
struct smb_request *req;
retry:
smb_setup_header(server, SMBsetattrE, 7, 0);
WSET(server->packet, smb_vwv0, SMB_I(inode)->fileid);
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
smb_setup_header(req, SMBsetattrE, 7, 0);
WSET(req->rq_header, smb_vwv0, SMB_I(inode)->fileid);
/* We don't change the creation time */
WSET(server->packet, smb_vwv1, 0);
WSET(server->packet, smb_vwv2, 0);
WSET(req->rq_header, smb_vwv1, 0);
WSET(req->rq_header, smb_vwv2, 0);
date_unix2dos(server, fattr->f_atime, &date, &time);
WSET(server->packet, smb_vwv3, date);
WSET(server->packet, smb_vwv4, time);
WSET(req->rq_header, smb_vwv3, date);
WSET(req->rq_header, smb_vwv4, time);
date_unix2dos(server, fattr->f_mtime, &date, &time);
WSET(server->packet, smb_vwv5, date);
WSET(server->packet, smb_vwv6, time);
WSET(req->rq_header, smb_vwv5, date);
WSET(req->rq_header, smb_vwv6, time);
#ifdef SMBFS_DEBUG_TIMESTAMP
printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n",
date, time, fattr->f_mtime);
#endif
result = smb_request_ok(server, SMBsetattrE, 0, 0);
if (result < 0) {
if (smb_retry(server))
goto retry;
goto out;
}
req->rq_flags |= SMB_REQ_NORETRY;
result = smb_request_ok(req, SMBsetattrE, 0, 0);
if (result < 0)
goto out_free;
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
/*
* Note: called with the server locked.
*
* Bugs Noted:
* (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't
* set the file's attribute flags.
......@@ -2787,20 +2771,21 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
struct dentry *dir, struct smb_fattr *fattr)
{
__u16 date, time;
char *p, *param = server->temp_buf;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
char *p, *param;
int result;
char data[26];
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
if (result < 0)
goto out;
goto out_free;
p = param + 6 + result;
WSET(data, 0, 0); /* creation time */
......@@ -2820,20 +2805,21 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
WSET(data, 20, 0); /* attr */
DSET(data, 22, 0); /* ULONG EA size */
result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
26, data, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
req->rq_trans2_command = TRANSACT2_SETPATHINFO;
req->rq_ldata = 26;
req->rq_data = data;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
goto out_free;
result = 0;
if (server->rcls != 0)
result = smb_errno(server);
if (req->rq_rcls != 0)
result = smb_errno(req);
out_free:
smb_rput(req);
out:
return result;
}
......@@ -2861,7 +2847,6 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
VERBOSE("setting %s/%s, open=%d\n",
DENTRY_PATH(dentry), smb_is_open(inode));
smb_lock_server(server);
/* setting the time on a Win95 server fails (tridge) */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
!(server->mnt->flags & SMB_MOUNT_WIN95)) {
......@@ -2890,7 +2875,6 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
}
}
smb_unlock_server(server);
return result;
}
......@@ -2901,26 +2885,25 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
int result;
char *p;
long unit;
struct smb_request *req;
smb_lock_server(server);
retry:
smb_setup_header(server, SMBdskattr, 0, 0);
if ((result = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
if (smb_retry(server))
goto retry;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 0)))
goto out;
}
p = SMB_VWV(server->packet);
smb_setup_header(req, SMBdskattr, 0, 0);
if ((result = smb_request_ok(req, SMBdskattr, 5, 0)) < 0)
goto out_free;
p = SMB_VWV(req->rq_header);
unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT;
attr->f_blocks = WVAL(p, 0) * unit;
attr->f_bsize = SMB_ST_BLKSIZE;
attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit;
result = 0;
out_free:
smb_rput(req);
out:
smb_unlock_server(server);
return result;
}
......
/*
* Autogenerated with cproto on: Thu Nov 22 21:18:04 CET 2001
* Autogenerated with cproto on: Fri Jul 12 22:15:26 CEST 2002
*/
struct smb_request;
/* proc.c */
extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
extern __u32 smb_len(__u8 *p);
extern int smb_get_rsize(struct smb_sb_info *server);
extern int smb_get_wsize(struct smb_sb_info *server);
extern int smb_errno(struct smb_sb_info *server);
extern int smb_errno(struct smb_request *req);
extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
extern int smb_wakeup(struct smb_sb_info *server);
extern __u8 *smb_setup_header(struct smb_sb_info *server, __u8 command, __u16 wct, __u16 bcc);
extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc);
extern int smb_open(struct dentry *dentry, int wish);
extern int smb_close(struct inode *ino);
extern int smb_close_fileid(struct dentry *dentry, __u16 fileid);
......@@ -36,19 +37,21 @@ extern void smb_invalidate_dircache_entries(struct dentry *parent);
extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos);
extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry);
/* sock.c */
extern void smb_data_ready(struct sock *sk, int len);
extern int smb_valid_socket(struct inode *inode);
extern int smb_catch_keepalive(struct smb_sb_info *server);
extern int smb_dont_catch_keepalive(struct smb_sb_info *server);
extern void smb_close_socket(struct smb_sb_info *server);
extern int smb_round_length(int len);
extern int smb_request(struct smb_sb_info *server);
extern int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param, int *lrdata, unsigned char **rdata, int *lrparam, unsigned char **rparam);
extern int smb_recv_available(struct smb_sb_info *server);
extern int smb_receive_header(struct smb_sb_info *server);
extern int smb_receive_drop(struct smb_sb_info *server);
extern int smb_receive(struct smb_sb_info *server, struct smb_request *req);
extern int smb_send_request(struct smb_request *req);
/* inode.c */
extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr);
extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr);
extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr);
extern void smb_invalidate_inodes(struct smb_sb_info *server);
extern int smb_revalidate_inode(struct dentry *dentry);
extern int smb_fill_super(struct super_block *sb, void *raw_data, int silent);
extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
extern int smb_notify_change(struct dentry *dentry, struct iattr *attr);
/* file.c */
......@@ -57,3 +60,19 @@ extern struct file_operations smb_file_operations;
extern struct inode_operations smb_file_inode_operations;
/* ioctl.c */
extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
/* smbiod.c */
extern void smbiod_wake_up(void);
extern void smbiod_register_server(struct smb_sb_info *server);
extern void smbiod_unregister_server(struct smb_sb_info *server);
extern void smbiod_flush(struct smb_sb_info *server);
extern int smbiod_retry(struct smb_sb_info *server);
/* request.c */
extern int smb_init_request_cache(void);
extern void smb_destroy_request_cache(void);
extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize);
extern void smb_rget(struct smb_request *req);
extern void smb_rput(struct smb_request *req);
extern int smb_add_request(struct smb_request *req);
extern int smb_request_send_req(struct smb_request *req);
extern int smb_request_send_server(struct smb_sb_info *server);
extern int smb_request_recv(struct smb_sb_info *server);
/*
* request.c
*
* Copyright (C) 2001 by Urban Widmark
*
* Please add a note about your changes to smbfs in the ChangeLog file.
*/
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smb_mount.h>
#include "smb_debug.h"
#include "request.h"
#include "proto.h"
/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */
#define SMB_SLAB_DEBUG 0
#define ROUND_UP(x) (((x)+3) & ~3)
/* cache for request structures */
static kmem_cache_t *req_cachep;
/*
/proc/slabinfo:
name, active, num, objsize, active_slabs, num_slaps, #pages
*/
int smb_init_request_cache(void)
{
req_cachep = kmem_cache_create("smb_request",
sizeof(struct smb_request), 0,
SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (req_cachep == NULL)
return -ENOMEM;
return 0;
}
void smb_destroy_request_cache(void)
{
if (kmem_cache_destroy(req_cachep))
printk(KERN_INFO "smb_destroy_request_cache: not all structures were freed\n");
}
/*
* Allocate and initialise a request structure
*/
static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server,
int bufsize)
{
struct smb_request *req;
unsigned char *buf = NULL;
req = kmem_cache_alloc(req_cachep, SLAB_KERNEL);
VERBOSE("allocating request: %p\n", req);
if (!req)
goto out;
if (bufsize > 0) {
buf = smb_kmalloc(bufsize, GFP_NOFS);
if (!buf) {
kmem_cache_free(req_cachep, req);
return NULL;
}
}
memset(req, 0, sizeof(struct smb_request));
req->rq_buffer = buf;
req->rq_bufsize = bufsize;
req->rq_server = server;
init_waitqueue_head(&req->rq_wait);
INIT_LIST_HEAD(&req->rq_queue);
atomic_set(&req->rq_count, 1);
out:
return req;
}
struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize)
{
struct smb_request *req = NULL;
for (;;) {
atomic_inc(&server->nr_requests);
if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) {
req = smb_do_alloc_request(server, bufsize);
if (req != NULL)
break;
}
#if 0
/*
* Try to free up at least one request in order to stay
* below the hard limit
*/
if (nfs_try_to_free_pages(server))
continue;
if (signalled() && (server->flags & NFS_MOUNT_INTR))
return ERR_PTR(-ERESTARTSYS);
current->policy = SCHED_YIELD;
schedule();
#else
/* FIXME: we want something like nfs does above, but that
requires changes to all callers and can wait. */
break;
#endif
}
return req;
}
static void smb_free_request(struct smb_request *req)
{
atomic_dec(&req->rq_server->nr_requests);
if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
smb_kfree(req->rq_buffer);
if (req->rq_trans2buffer)
smb_kfree(req->rq_trans2buffer);
kmem_cache_free(req_cachep, req);
}
/*
* What prevents a rget to race with a rput? The count must never drop to zero
* while it is in use. Only rput if it is ok that it is free'd.
*/
void smb_rget(struct smb_request *req)
{
atomic_inc(&req->rq_count);
}
void smb_rput(struct smb_request *req)
{
if (atomic_dec_and_test(&req->rq_count)) {
list_del_init(&req->rq_queue);
smb_free_request(req);
}
}
/* setup to receive the data part of the SMB */
static int smb_setup_bcc(struct smb_request *req)
{
int result = 0;
req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;
if (req->rq_rlen > req->rq_bufsize) {
PARANOIA("Packet too large %d > %d\n",
req->rq_rlen, req->rq_bufsize);
return -ENOBUFS;
}
req->rq_iov[0].iov_base = req->rq_buffer;
req->rq_iov[0].iov_len = req->rq_rlen;
req->rq_iovlen = 1;
return result;
}
/*
* Prepare a "normal" request structure.
*/
static int smb_setup_request(struct smb_request *req)
{
int len = smb_len(req->rq_header) + 4;
req->rq_slen = len;
/* if we expect a data part in the reply we set the iov's to read it */
if (req->rq_resp_bcc)
req->rq_setup_read = smb_setup_bcc;
/* This tries to support re-using the same request */
req->rq_bytes_sent = 0;
req->rq_rcls = 0;
req->rq_err = 0;
req->rq_errno = 0;
req->rq_fragment = 0;
if (req->rq_trans2buffer)
smb_kfree(req->rq_trans2buffer);
return 0;
}
/*
* Prepare a transaction2 request structure
*/
static int smb_setup_trans2request(struct smb_request *req)
{
struct smb_sb_info *server = req->rq_server;
int mparam, mdata;
static unsigned char padding[4] = { 0, };
/* 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 header = SMB_HEADER_LEN + 2 * smb_parameters + 2;
const int oparam = ROUND_UP(header + 3);
const int odata = ROUND_UP(oparam + req->rq_lparm);
const int bcc = (req->rq_data ? odata + req->rq_ldata :
oparam + req->rq_lparm) - header;
if ((bcc + oparam) > server->opt.max_xmit)
return -ENOMEM;
smb_setup_header(req, SMBtrans2, smb_parameters, bcc);
/*
* max parameters + max data + max setup == bufsize to make NT4 happy
* and not abort the transfer or split into multiple responses. It also
* makes smbfs happy as handling packets larger than the buffer size
* is extra work.
*
* OS/2 is probably going to hate me for this ...
*/
mparam = SMB_TRANS2_MAX_PARAM;
mdata = req->rq_bufsize - mparam;
mdata = server->opt.max_xmit - mparam - 100;
if (mdata < 1024) {
mdata = 1024;
mparam = 20;
}
#if 0
/* NT/win2k has ~4k max_xmit, so with this we request more than it wants
to return as one SMB. Useful for testing the fragmented trans2
handling. */
mdata = 8192;
#endif
WSET(req->rq_header, smb_tpscnt, req->rq_lparm);
WSET(req->rq_header, smb_tdscnt, req->rq_ldata);
WSET(req->rq_header, smb_mprcnt, mparam);
WSET(req->rq_header, smb_mdrcnt, mdata);
WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */
WSET(req->rq_header, smb_flags, 0);
DSET(req->rq_header, smb_timeout, 0);
WSET(req->rq_header, smb_pscnt, req->rq_lparm);
WSET(req->rq_header, smb_psoff, oparam - 4);
WSET(req->rq_header, smb_dscnt, req->rq_ldata);
WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0);
*(req->rq_header + smb_suwcnt) = 0x01; /* setup count */
*(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */
WSET(req->rq_header, smb_setup0, req->rq_trans2_command);
req->rq_iovlen = 2;
req->rq_iov[0].iov_base = (void *) req->rq_header;
req->rq_iov[0].iov_len = oparam;
req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm;
req->rq_iov[1].iov_len = req->rq_lparm;
req->rq_slen = oparam + req->rq_lparm;
if (req->rq_data) {
req->rq_iovlen += 2;
req->rq_iov[2].iov_base = padding;
req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm;
req->rq_iov[3].iov_base = req->rq_data;
req->rq_iov[3].iov_len = req->rq_ldata;
req->rq_slen = odata + req->rq_ldata;
}
/* always a data part for trans2 replies */
req->rq_setup_read = smb_setup_bcc;
return 0;
}
/*
* Add a request and tell smbiod to process it
*/
int smb_add_request(struct smb_request *req)
{
long timeleft;
struct smb_sb_info *server = req->rq_server;
int result = 0;
smb_setup_request(req);
if (req->rq_trans2_command) {
if (req->rq_buffer == NULL) {
PARANOIA("trans2 attempted without response buffer!\n");
return -EIO;
}
result = smb_setup_trans2request(req);
}
if (result < 0)
return result;
#ifdef SMB_DEBUG_PACKET_SIZE
add_xmit_stats(req);
#endif
/* add 'req' to the queue of requests */
if (smb_lock_server_interruptible(server))
return -EINTR;
/*
* Try to send the request as the process. If that fails we queue the
* request and let smbiod send it later.
*/
/* FIXME: each server has a number on the maximum number of parallel
requests. 10, 50 or so. We should not allow more requests to be
active. */
if (server->mid > 0xf000)
server->mid = 0;
req->rq_mid = server->mid++;
WSET(req->rq_header, smb_mid, req->rq_mid);
result = 0;
if (server->state == CONN_VALID) {
if (list_empty(&server->xmitq))
result = smb_request_send_req(req);
if (result < 0) {
/* Connection lost? */
server->conn_error = result;
server->state = CONN_INVALID;
}
}
if (result != 1)
list_add_tail(&req->rq_queue, &server->xmitq);
smb_rget(req);
if (server->state != CONN_VALID)
smbiod_retry(server);
smb_unlock_server(server);
smbiod_wake_up();
/* FIXME: replace with a timeout-able wake_event_interruptible */
timeleft = interruptible_sleep_on_timeout(&req->rq_wait, 30*HZ);
if (!timeleft || signal_pending(current)) {
/*
* On timeout or on interrupt we want to try and remove the
* request from the recvq/xmitq.
*/
smb_lock_server(server);
if (!(req->rq_flags & SMB_REQ_RECEIVED)) {
list_del_init(&req->rq_queue);
smb_rput(req);
}
smb_unlock_server(server);
}
if (!timeleft) {
PARANOIA("request [%p, mid=%d] timed out!\n",
req, req->rq_mid);
VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com));
VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls));
VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg));
VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid));
VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid));
VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid));
VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid));
VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct));
req->rq_rcls = ERRSRV;
req->rq_err = ERRtimeout;
/* Just in case it was "stuck" */
smbiod_wake_up();
}
VERBOSE("woke up, rcls=%d\n", req->rq_rcls);
if (req->rq_rcls != 0)
req->rq_errno = smb_errno(req);
if (signal_pending(current))
req->rq_errno = -ERESTARTSYS;
return req->rq_errno;
}
/*
* Send a request and place it on the recvq if successfully sent.
* Must be called with the server lock held.
*/
int smb_request_send_req(struct smb_request *req)
{
struct smb_sb_info *server = req->rq_server;
int result;
result = smb_send_request(req);
if (result < 0 && result != -EAGAIN)
goto out;
result = 0;
if (!(req->rq_flags & SMB_REQ_TRANSMITTED))
goto out;
list_del_init(&req->rq_queue);
list_add_tail(&req->rq_queue, &server->recvq);
result = 1;
out:
return result;
}
/*
* Sends one request for this server. (smbiod)
* Must be called with the server lock held.
* Returns: <0 on error
* 0 if no request could be completely sent
* 1 if all data for one request was sent
*/
int smb_request_send_server(struct smb_sb_info *server)
{
struct list_head *head;
struct smb_request *req;
int result;
if (server->state != CONN_VALID)
return 0;
/* dequeue first request, if any */
req = NULL;
head = server->xmitq.next;
if (head != &server->xmitq) {
req = list_entry(head, struct smb_request, rq_queue);
}
if (!req)
return 0;
result = smb_request_send_req(req);
if (result < 0) {
server->conn_error = result;
list_del_init(&req->rq_queue);
list_add(&req->rq_queue, &server->xmitq);
result = -EIO;
goto out;
}
out:
return result;
}
/*
* Try to find a request matching this "mid". Typically the first entry will
* be the matching one.
*/
static struct smb_request *find_request(struct smb_sb_info *server, int mid)
{
struct list_head *tmp;
struct smb_request *req = NULL;
list_for_each(tmp, &server->recvq) {
req = list_entry(tmp, struct smb_request, rq_queue);
if (req->rq_mid == mid) {
break;
}
req = NULL;
}
if (!req) {
VERBOSE("received reply with mid %d but no request!\n",
WVAL(server->header, smb_mid));
server->rstate = SMB_RECV_DROP;
}
return req;
}
/*
* Called when we have read the smb header and believe this is a response.
*/
static int smb_init_request(struct smb_sb_info *server, struct smb_request *req)
{
int hdrlen, wct;
memcpy(req->rq_header, server->header, SMB_HEADER_LEN);
wct = *(req->rq_header + smb_wct);
if (wct > 20) {
PARANOIA("wct too large, %d > 20\n", wct);
server->rstate = SMB_RECV_DROP;
return 0;
}
req->rq_resp_wct = wct;
hdrlen = SMB_HEADER_LEN + wct*2 + 2;
VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct);
req->rq_bytes_recvd = SMB_HEADER_LEN;
req->rq_rlen = hdrlen;
req->rq_iov[0].iov_base = req->rq_header;
req->rq_iov[0].iov_len = hdrlen;
req->rq_iovlen = 1;
server->rstate = SMB_RECV_PARAM;
#ifdef SMB_DEBUG_PACKET_SIZE
add_recv_stats(smb_len(server->header));
#endif
return 0;
}
/*
* Reads the SMB parameters
*/
static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req)
{
int result;
result = smb_receive(server, req);
if (result < 0)
return result;
if (req->rq_bytes_recvd < req->rq_rlen)
return 0;
VERBOSE("result: %d smb_bcc: %04x\n", result,
WVAL(req->rq_header, SMB_HEADER_LEN +
(*(req->rq_header + smb_wct) * 2)));
result = 0;
req->rq_iov[0].iov_base = NULL;
req->rq_rlen = 0;
if (req->rq_callback)
req->rq_callback(req);
else if (req->rq_setup_read)
result = req->rq_setup_read(req);
if (result < 0) {
server->rstate = SMB_RECV_DROP;
return result;
}
server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END;
req->rq_bytes_recvd = 0; // recvd out of the iov
VERBOSE("rlen: %d\n", req->rq_rlen);
if (req->rq_rlen < 0) {
PARANOIA("Parameters read beyond end of packet!\n");
server->rstate = SMB_RECV_END;
return -EIO;
}
return 0;
}
/*
* Reads the SMB data
*/
static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req)
{
int result;
result = smb_receive(server, req);
if (result < 0)
goto out;
if (req->rq_bytes_recvd < req->rq_rlen)
goto out;
server->rstate = SMB_RECV_END;
out:
VERBOSE("result: %d\n", result);
return result;
}
/*
* Receive a transaction2 response
* Return: 0 if the response has been fully read
* 1 if there are further "fragments" to read
* <0 if there is an error
*/
static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
{
unsigned char *inbuf;
unsigned int parm_disp, parm_offset, parm_count, parm_tot;
unsigned int data_disp, data_offset, data_count, data_tot;
int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2;
VERBOSE("handling trans2\n");
inbuf = req->rq_header;
data_tot = WVAL(inbuf, smb_tdrcnt);
parm_tot = WVAL(inbuf, smb_tprcnt);
parm_disp = WVAL(inbuf, smb_prdisp);
parm_offset = WVAL(inbuf, smb_proff);
parm_count = WVAL(inbuf, smb_prcnt);
data_disp = WVAL(inbuf, smb_drdisp);
data_offset = WVAL(inbuf, smb_droff);
data_count = WVAL(inbuf, smb_drcnt);
/* Modify offset for the split header/buffer we use */
data_offset -= hdrlen;
parm_offset -= hdrlen;
if (parm_count == parm_tot && data_count == data_tot) {
/*
* This packet has all the trans2 data.
*
* We setup the request so that this will be the common
* case. It may be a server error to not return a
* response that fits.
*/
VERBOSE("single trans2 response "
"dcnt=%d, pcnt=%d, doff=%d, poff=%d\n",
data_count, parm_count,
data_offset, parm_offset);
req->rq_ldata = data_count;
req->rq_lparm = parm_count;
req->rq_data = req->rq_buffer + data_offset;
req->rq_parm = req->rq_buffer + parm_offset;
return 0;
}
VERBOSE("multi trans2 response "
"frag=%d, dcnt=%d, pcnt=%d, doff=%d, poff=%d\n",
req->rq_fragment,
data_count, parm_count,
data_offset, parm_offset);
if (!req->rq_fragment) {
int buf_len;
/* We got the first trans2 fragment */
req->rq_fragment = 1;
req->rq_total_data = data_tot;
req->rq_total_parm = parm_tot;
req->rq_ldata = 0;
req->rq_lparm = 0;
buf_len = data_tot + parm_tot;
if (buf_len > SMB_MAX_PACKET_SIZE)
goto out_too_long;
req->rq_trans2bufsize = buf_len;
req->rq_trans2buffer = smb_kmalloc(buf_len, GFP_NOFS);
if (!req->rq_trans2buffer)
goto out_no_mem;
req->rq_parm = req->rq_trans2buffer;
req->rq_data = req->rq_trans2buffer + parm_tot;
} else if (req->rq_total_data < data_tot ||
req->rq_total_parm < parm_tot)
goto out_data_grew;
if (parm_disp + parm_count > req->rq_total_parm)
goto out_bad_parm;
if (data_disp + data_count > req->rq_total_data)
goto out_bad_data;
inbuf = req->rq_buffer;
memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count);
memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count);
req->rq_ldata += data_count;
req->rq_lparm += parm_count;
/*
* Check whether we've received all of the data. Note that
* we use the packet totals -- total lengths might shrink!
*/
if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot)
return 0;
return 1;
out_too_long:
printk(KERN_ERR "smb_trans2: data/param too long, data=%d, parm=%d\n",
data_tot, parm_tot);
req->rq_errno = -EIO;
goto out;
out_no_mem:
printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n",
req->rq_trans2bufsize);
req->rq_errno = -ENOMEM;
goto out;
out_data_grew:
printk(KERN_ERR "smb_trans2: data/params grew!\n");
req->rq_errno = -EIO;
goto out;
out_bad_parm:
printk(KERN_ERR "smb_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
parm_disp, parm_count, parm_tot);
req->rq_errno = -EIO;
goto out;
out_bad_data:
printk(KERN_ERR "smb_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
data_disp, data_count, data_tot);
req->rq_errno = -EIO;
out:
return req->rq_errno;
}
/*
* State machine for receiving responses. We handle the fact that we can't
* read the full response in one try by having states telling us how much we
* have read.
*
* Must be called with the server lock held (only called from smbiod).
*
* Return: <0 on error
*/
int smb_request_recv(struct smb_sb_info *server)
{
struct smb_request *req = NULL;
int result = 0;
if (smb_recv_available(server) <= 0)
return 0;
VERBOSE("state: %d\n", server->rstate);
switch (server->rstate) {
case SMB_RECV_DROP:
result = smb_receive_drop(server);
if (result < 0)
break;
if (server->rstate == SMB_RECV_DROP)
break;
server->rstate = SMB_RECV_START;
/* fallthrough */
case SMB_RECV_START:
server->smb_read = 0;
server->rstate = SMB_RECV_HEADER;
/* fallthrough */
case SMB_RECV_HEADER:
result = smb_receive_header(server);
if (result < 0)
break;
if (server->rstate == SMB_RECV_HEADER)
break;
if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) {
server->rstate = SMB_RECV_REQUEST;
break;
}
if (server->rstate != SMB_RECV_HCOMPLETE)
break;
/* fallthrough */
case SMB_RECV_HCOMPLETE:
req = find_request(server, WVAL(server->header, smb_mid));
if (!req)
break;
smb_init_request(server, req);
req->rq_rcls = *(req->rq_header + smb_rcls);
req->rq_err = WVAL(req->rq_header, smb_err);
if (server->rstate != SMB_RECV_PARAM)
break;
/* fallthrough */
case SMB_RECV_PARAM:
if (!req)
req = find_request(server,WVAL(server->header,smb_mid));
if (!req)
break;
result = smb_recv_param(server, req);
if (result < 0)
break;
if (server->rstate != SMB_RECV_DATA)
break;
/* fallthrough */
case SMB_RECV_DATA:
if (!req)
req = find_request(server,WVAL(server->header,smb_mid));
if (!req)
break;
result = smb_recv_data(server, req);
if (result < 0)
break;
break;
/* We should never be called with any of these states */
case SMB_RECV_END:
case SMB_RECV_REQUEST:
server->rstate = SMB_RECV_END;
break;
}
if (result < 0) {
/* We saw an error */
return result;
}
if (server->rstate != SMB_RECV_END)
return 0;
result = 0;
if (req->rq_trans2_command && req->rq_rcls == SUCCESS)
result = smb_recv_trans2(server, req);
/*
* Response completely read. Drop any extra bytes sent by the server.
* (Yes, servers sometimes add extra bytes to requests)
*/
VERBOSE("smb_len: %d smb_read: %d\n",
server->smb_len, server->smb_read);
if (server->smb_read < server->smb_len)
smb_receive_drop(server);
server->rstate = SMB_RECV_START;
if (!result) {
list_del_init(&req->rq_queue);
req->rq_flags |= SMB_REQ_RECEIVED;
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
return 0;
}
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/list.h>
struct smb_request {
struct list_head rq_queue; /* recvq or xmitq for the server */
atomic_t rq_count;
wait_queue_head_t rq_wait;
int rq_flags;
int rq_mid; /* multiplex ID, set by request.c */
struct smb_sb_info *rq_server;
/* header + word count + parameter words + byte count */
unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2];
int rq_bufsize;
unsigned char *rq_buffer;
/* FIXME: this is not good enough for merging IO requests. */
unsigned char *rq_page;
int rq_rsize;
int rq_resp_wct;
int rq_resp_bcc;
int rq_rlen;
int rq_bytes_recvd;
int rq_slen;
int rq_bytes_sent;
int rq_iovlen;
struct iovec rq_iov[4];
int (*rq_setup_read) (struct smb_request *);
void (*rq_callback) (struct smb_request *);
/* ------ trans2 stuff ------ */
u16 rq_trans2_command; /* 0 if not a trans2 request */
unsigned int rq_ldata;
unsigned char *rq_data;
unsigned int rq_lparm;
unsigned char *rq_parm;
int rq_fragment;
u32 rq_total_data;
u32 rq_total_parm;
int rq_trans2bufsize;
unsigned char *rq_trans2buffer;
/* ------ response ------ */
unsigned short rq_rcls;
unsigned short rq_err;
int rq_errno;
};
#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */
#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */
#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */
#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */
#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */
#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */
/*
* smbiod.c
*
* Copyright (C) 2000, Charles Loep / Corel Corp.
* Copyright (C) 2001, Urban Widmark
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/dcache.h>
#include <linux/smp_lock.h>
#include <net/ip.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smb_mount.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "smb_debug.h"
#include "request.h"
#include "proto.h"
static int smbiod_pid = -1;
static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait);
static LIST_HEAD(smb_servers);
static spinlock_t servers_lock = SPIN_LOCK_UNLOCKED;
#define SMBIOD_DATA_READY (1<<0)
static long smbiod_flags;
static int smbiod(void *);
static void smbiod_start(void);
static void smbiod_stop(void);
/*
* called when there's work for us to do
*/
void smbiod_wake_up()
{
if (smbiod_pid == -1)
return;
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
wake_up_interruptible(&smbiod_wait);
}
/*
* start smbiod if none is running
*/
static void smbiod_start()
{
if (smbiod_pid != -1)
return;
smbiod_pid = kernel_thread(smbiod, NULL, 0);
}
/*
* stop smbiod if there are no open connections
*/
static void smbiod_stop()
{
if (smbiod_pid != -1 && list_empty(&smb_servers))
kill_proc(smbiod_pid, SIGKILL, 1);
}
/*
* register a server & start smbiod if necessary
*/
void smbiod_register_server(struct smb_sb_info *server)
{
spin_lock(&servers_lock);
list_add(&server->entry, &smb_servers);
VERBOSE("%p\n", server);
smbiod_start();
spin_unlock(&servers_lock);
}
/*
* unregister a server & stop smbiod if necessary
*/
void smbiod_unregister_server(struct smb_sb_info *server)
{
spin_lock(&servers_lock);
list_del_init(&server->entry);
VERBOSE("%p\n", server);
smbiod_stop();
spin_unlock(&servers_lock);
smb_lock_server(server);
smbiod_flush(server);
smb_unlock_server(server);
}
void smbiod_flush(struct smb_sb_info *server)
{
struct list_head *tmp, *n;
struct smb_request *req;
list_for_each_safe(tmp, n, &server->xmitq) {
req = list_entry(tmp, struct smb_request, rq_queue);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
list_for_each_safe(tmp, n, &server->recvq) {
req = list_entry(tmp, struct smb_request, rq_queue);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
}
/*
* Wake up smbmount and make it reconnect to the server.
* This must be called with the server locked.
*
* FIXME: add smbconnect version to this
*/
int smbiod_retry(struct smb_sb_info *server)
{
struct list_head *head;
struct smb_request *req;
pid_t pid = server->conn_pid;
int result = 0;
VERBOSE("state: %d\n", server->state);
if (server->state == CONN_VALID || server->state == CONN_RETRYING)
goto out;
smb_invalidate_inodes(server);
/*
* Some requests are meaningless after a retry, so we abort them.
* One example are all requests using 'fileid' since the files are
* closed on retry.
*/
head = server->xmitq.next;
while (head != &server->xmitq) {
req = list_entry(head, struct smb_request, rq_queue);
head = head->next;
if (req->rq_flags & SMB_REQ_NORETRY) {
VERBOSE("aborting request %p on xmitq\n", req);
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
}
/*
* FIXME: test the code for retrying request we already sent
*/
head = server->recvq.next;
while (head != &server->recvq) {
req = list_entry(head, struct smb_request, rq_queue);
head = head->next;
#if 0
if (req->rq_flags & SMB_REQ_RETRY) {
/* must move the request to the xmitq */
VERBOSE("retrying request %p on recvq\n", req);
list_del(&req->rq_queue);
list_add(&req->rq_queue, &server->xmitq);
continue;
}
#endif
VERBOSE("aborting request %p on recvq\n", req);
/* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
req->rq_errno = -EIO;
list_del_init(&req->rq_queue);
smb_rput(req);
wake_up_interruptible(&req->rq_wait);
}
smb_close_socket(server);
if (pid == 0) {
/* FIXME: this is fatal, umount? */
printk(KERN_ERR "smb_retry: no connection process\n");
server->state = CONN_RETRIED;
goto out;
}
/*
* Change state so that only one retry per server will be started.
*/
server->state = CONN_RETRYING;
/*
* Note: use the "priv" flag, as a user process may need to reconnect.
*/
result = kill_proc(pid, SIGUSR1, 1);
if (result) {
/* FIXME: this is most likely fatal, umount? */
printk(KERN_ERR "smb_retry: signal failed [%d]\n", result);
goto out;
}
VERBOSE("signalled pid %d\n", pid);
/* FIXME: The retried requests should perhaps get a "time boost". */
out:
return result;
}
/*
* Currently handles lockingX packets.
*/
static void smbiod_handle_request(struct smb_sb_info *server)
{
PARANOIA("smbiod got a request ... and we don't implement oplocks!\n");
server->rstate = SMB_RECV_DROP;
}
/*
* Do some IO for one server.
*/
static void smbiod_doio(struct smb_sb_info *server)
{
int result;
int maxwork = 7;
if (server->state != CONN_VALID)
goto out;
do {
result = smb_request_recv(server);
if (result < 0) {
server->state = CONN_INVALID;
smbiod_retry(server);
goto out; /* reconnecting is slow */
} else if (server->rstate == SMB_RECV_REQUEST)
smbiod_handle_request(server);
} while (result > 0 && maxwork-- > 0);
/*
* If there is more to read then we want to be sure to wake up again.
*/
if (server->state != CONN_VALID)
goto out;
if (smb_recv_available(server) > 0)
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
do {
result = smb_request_send_server(server);
if (result < 0) {
server->state = CONN_INVALID;
smbiod_retry(server);
goto out; /* reconnecting is slow */
}
} while (result > 0);
/*
* If the last request was not sent out we want to wake up again.
*/
if (!list_empty(&server->xmitq))
set_bit(SMBIOD_DATA_READY, &smbiod_flags);
out:
}
/*
* smbiod kernel thread
*/
static int smbiod(void *unused)
{
daemonize();
spin_lock_irq(&current->sigmask_lock);
siginitsetinv(&current->blocked, sigmask(SIGKILL));
recalc_sigpending();
spin_unlock_irq(&current->sigmask_lock);
strcpy(current->comm, "smbiod");
VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid);
for (;;) {
struct smb_sb_info *server;
struct list_head *pos, *n;
/* FIXME: Use poll? */
wait_event_interruptible(smbiod_wait,
test_bit(SMBIOD_DATA_READY, &smbiod_flags));
if (signal_pending(current))
break;
clear_bit(SMBIOD_DATA_READY, &smbiod_flags);
/*
* We must hold the servers_lock while looking for servers
* to check or else we have a race with put_super.
*/
spin_lock(&servers_lock);
list_for_each_safe(pos, n, &smb_servers) {
server = list_entry(pos, struct smb_sb_info, entry);
VERBOSE("checking server %p\n", server);
smb_lock_server(server);
spin_unlock(&servers_lock);
smbiod_doio(server);
smb_unlock_server(server);
spin_lock(&servers_lock);
}
spin_unlock(&servers_lock);
}
VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid);
smbiod_pid = -1;
return 0;
}
......@@ -15,6 +15,7 @@
#include <linux/file.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/tcp.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/smp_lock.h>
......@@ -27,19 +28,26 @@
#include <linux/smbno.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include "smb_debug.h"
#include "proto.h"
#include "request.h"
static int
_recvfrom(struct socket *socket, unsigned char *ubuf, int size,
unsigned flags)
_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
{
struct iovec iov;
struct msghdr msg;
struct scm_cookie scm;
mm_segment_t fs;
fs = get_fs();
set_fs(get_ds());
flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
msg.msg_flags = flags;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
......@@ -48,130 +56,36 @@ _recvfrom(struct socket *socket, unsigned char *ubuf, int size,
iov.iov_base = ubuf;
iov.iov_len = size;
memset(&scm, 0,sizeof(scm));
size=socket->ops->recvmsg(socket, &msg, size, flags, &scm);
if(size>=0)
scm_recv(socket,&msg,&scm,flags);
return size;
}
static int
_send(struct socket *socket, const void *buff, int len)
{
struct iovec iov;
struct msghdr msg;
struct scm_cookie scm;
int err;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
iov.iov_base = (void *)buff;
iov.iov_len = len;
msg.msg_flags = 0;
memset(&scm, 0, sizeof(scm));
size = socket->ops->recvmsg(socket, &msg, size, flags, &scm);
if (size >= 0)
scm_recv(socket, &msg, &scm, flags);
err = scm_send(socket, &msg, &scm);
if (err >= 0)
{
err = socket->ops->sendmsg(socket, &msg, len, &scm);
scm_destroy(&scm);
}
return err;
set_fs(fs);
return size;
}
struct data_callback {
struct tq_struct cb;
struct sock *sk;
};
/*
* N.B. What happens if we're in here when the socket closes??
* Return the server this socket belongs to
*/
static void
found_data(struct sock *sk)
static struct smb_sb_info *
server_from_socket(struct socket *socket)
{
/*
* FIXME: copied from sock_def_readable, it should be a call to
* server->data_ready() -- manfreds@colorfullife.com
*/
read_lock(&sk->callback_lock);
if(!sk->dead) {
wake_up_interruptible(sk->sleep);
sock_wake_async(sk->socket,1,POLL_IN);
}
read_unlock(&sk->callback_lock);
}
static void
smb_data_callback(void* ptr)
{
struct data_callback* job=ptr;
struct socket *socket = job->sk->socket;
unsigned char peek_buf[4];
int result = 0;
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 (count-- > 0) {
peek_buf[0] = 0;
result = -EIO;
if (job->sk->dead) {
PARANOIA("sock dead!\n");
break;
}
result = _recvfrom(socket, (void *) peek_buf, 1,
MSG_PEEK | MSG_DONTWAIT);
if (result < 0)
break;
if (peek_buf[0] != 0x85)
break;
/* got SESSION KEEP ALIVE */
result = _recvfrom(socket, (void *) peek_buf, 4,
MSG_DONTWAIT);
DEBUG1("got SESSION KEEPALIVE\n");
if (result < 0)
break;
found = 1;
}
unlock_kernel();
set_fs(fs);
DEBUG1("found=%d, count=%d, result=%d\n", found, count, result);
if (found)
found_data(job->sk);
smb_kfree(ptr);
return socket->sk->user_data;
}
static void
/*
* Called when there is data on the socket.
*/
void
smb_data_ready(struct sock *sk, int len)
{
struct data_callback* job;
job = smb_kmalloc(sizeof(struct data_callback),GFP_ATOMIC);
if(job == 0) {
printk("smb_data_ready: lost SESSION KEEPALIVE due to OOM.\n");
found_data(sk);
return;
}
INIT_LIST_HEAD(&job->cb.list);
job->cb.sync = 0;
job->cb.routine = smb_data_callback;
job->cb.data = job;
job->sk = sk;
schedule_task(&job->cb);
struct smb_sb_info *server = server_from_socket(sk->socket);
void (*data_ready)(struct sock *, int) = server->data_ready;
data_ready(sk, len);
VERBOSE("(%p, %d)\n", sk, len);
smbiod_wake_up();
}
int
......@@ -197,691 +111,336 @@ server_sock(struct smb_sb_info *server)
return NULL;
}
int
smb_catch_keepalive(struct smb_sb_info *server)
{
struct socket *socket;
struct sock *sk;
void *data_ready;
int error;
error = -EINVAL;
socket = server_sock(server);
if (!socket)
{
printk(KERN_DEBUG "smb_catch_keepalive: did not get valid server!\n");
server->data_ready = NULL;
goto out;
}
sk = socket->sk;
if (sk == NULL)
{
DEBUG1("sk == NULL");
server->data_ready = NULL;
goto out;
}
DEBUG1("sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
/*
* Install the callback atomically to avoid races ...
*/
data_ready = xchg(&sk->data_ready, smb_data_ready);
if (data_ready != smb_data_ready) {
server->data_ready = data_ready;
error = 0;
} else
printk(KERN_ERR "smb_catch_keepalive: already done\n");
out:
return error;
}
int
smb_dont_catch_keepalive(struct smb_sb_info *server)
{
struct socket *socket;
struct sock *sk;
void * data_ready;
int error;
error = -EINVAL;
socket = server_sock(server);
if (!socket)
{
printk(KERN_DEBUG "smb_dont_catch_keepalive: did not get valid server!\n");
goto out;
}
sk = socket->sk;
if (sk == NULL)
{
DEBUG1("sk == NULL");
goto out;
}
/* Is this really an error?? */
if (server->data_ready == NULL)
{
printk(KERN_DEBUG "smb_dont_catch_keepalive: "
"server->data_ready == NULL\n");
goto out;
}
DEBUG1("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
/*
* Restore the original callback atomically to avoid races ...
*/
data_ready = xchg(&sk->data_ready, server->data_ready);
server->data_ready = NULL;
if (data_ready != smb_data_ready)
{
printk(KERN_ERR "smb_dont_catch_keepalive: "
"sk->data_ready != smb_data_ready\n");
}
error = 0;
out:
return error;
}
/*
* Called with the server locked.
*/
void
smb_close_socket(struct smb_sb_info *server)
{
struct file * file = server->sock_file;
if (file)
{
VERBOSE("closing socket %p\n", server_sock(server));
#ifdef SMBFS_PARANOIA
if (server_sock(server)->sk->data_ready == smb_data_ready)
PARANOIA("still catching keepalives!\n");
#endif
if (file) {
struct socket *sock = server_sock(server);
VERBOSE("closing socket %p\n", sock);
sock->sk->data_ready = server->data_ready;
server->sock_file = NULL;
fput(file);
}
}
static int
smb_send_raw(struct socket *socket, unsigned char *source, int length)
{
int result;
int already_sent = 0;
while (already_sent < length)
{
result = _send(socket,
(void *) (source + already_sent),
length - already_sent);
if (result == 0)
{
return -EIO;
}
if (result < 0)
{
DEBUG1("smb_send_raw: sendto error = %d\n", -result);
return result;
}
already_sent += result;
}
return already_sent;
}
static int
smb_receive_raw(struct socket *socket, unsigned char *target, int length)
{
int result;
int already_read = 0;
while (already_read < length)
{
result = _recvfrom(socket,
(void *) (target + already_read),
length - already_read, 0);
if (result == 0)
{
return -EIO;
}
if (result < 0)
{
DEBUG1("recvfrom error = %d\n", -result);
return result;
}
already_read += result;
}
return already_read;
}
static int
smb_get_length(struct socket *socket, unsigned char *header)
{
int result;
unsigned char peek_buf[4];
mm_segment_t fs;
re_recv:
fs = get_fs();
set_fs(get_ds());
result = smb_receive_raw(socket, peek_buf, 4);
set_fs(fs);
if (result < 0)
{
result = _recvfrom(socket, header, 4, MSG_PEEK);
if (result == -EAGAIN)
return -ENODATA;
if (result < 0) {
PARANOIA("recv error = %d\n", -result);
return result;
}
switch (peek_buf[0])
{
if (result < 4)
return -ENODATA;
switch (header[0]) {
case 0x00:
case 0x82:
break;
case 0x85:
DEBUG1("Got SESSION KEEP ALIVE\n");
goto re_recv;
_recvfrom(socket, header, 4, 0); /* read away */
return -ENODATA;
default:
PARANOIA("Invalid NBT packet, code=%x\n", peek_buf[0]);
PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
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);
return smb_len(header);
}
/*
* Since we allocate memory in increments of PAGE_SIZE,
* round up the packet length to the next multiple.
*/
int
smb_round_length(int len)
smb_recv_available(struct smb_sb_info *server)
{
return (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
mm_segment_t oldfs;
int avail, err;
struct socket *sock = server_sock(server);
oldfs = get_fs();
set_fs(get_ds());
err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
set_fs(oldfs);
return (err >= 0) ? avail : err;
}
/*
* smb_receive
* fs points to the correct segment
* Adjust the iovec to move on 'n' bytes (from nfs/sunrpc)
*/
static int
smb_receive(struct smb_sb_info *server)
smb_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
{
struct socket *socket = server_sock(server);
unsigned char * packet = server->packet;
int len, result;
unsigned char peek_buf[4];
struct iovec *iv = msg->msg_iov;
int i;
int len;
result = smb_get_length(socket, peek_buf);
if (result < 0)
goto out;
len = result;
/*
* Some servers do not respect our max_xmit and send
* larger packets. Try to allocate a new packet,
* but don't free the old one unless we succeed.
* Eat any sent iovecs
*/
if (len + 4 > server->packet_size)
{
int new_len = smb_round_length(len + 4);
result = -ENOMEM;
packet = smb_vmalloc(new_len);
if (packet == NULL)
goto out;
smb_vfree(server->packet);
server->packet = packet;
server->packet_size = new_len;
while (iv->iov_len <= amount) {
amount -= iv->iov_len;
iv++;
msg->msg_iovlen--;
}
memcpy(packet, peek_buf, 4);
result = smb_receive_raw(socket, packet + 4, len);
if (result < 0)
{
VERBOSE("receive error: %d\n", result);
goto out;
/*
* And chew down the partial one
*/
niv[0].iov_len = iv->iov_len-amount;
niv[0].iov_base =((unsigned char *)iv->iov_base)+amount;
iv++;
len = niv[0].iov_len;
/*
* And copy any others
*/
for (i = 1; i < msg->msg_iovlen; i++) {
niv[i] = *iv++;
len += niv[i].iov_len;
}
server->rcls = *(packet + smb_rcls);
server->err = WVAL(packet, smb_err);
#ifdef SMBFS_DEBUG_VERBOSE
if (server->rcls != 0)
VERBOSE("rcls=%d, err=%d\n", server->rcls, server->err);
#endif
out:
return result;
msg->msg_iov = niv;
return len;
}
/*
* This routine checks first for "fast track" processing, as most
* packets won't need to be copied. Otherwise, it allocates a new
* packet to hold the incoming data.
*
* Note that the final server packet must be the larger of the two;
* server packets aren't allowed to shrink.
* smb_receive_header
* Only called by the smbiod thread.
*/
static int
smb_receive_trans2(struct smb_sb_info *server,
int *ldata, unsigned char **data,
int *lparm, unsigned char **parm)
int
smb_receive_header(struct smb_sb_info *server)
{
unsigned char *inbuf, *base, *rcv_buf = NULL;
unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
unsigned int total_p = 0, total_d = 0, buf_len = 0;
int result;
struct socket *sock;
int result = 0;
unsigned char peek_buf[4];
while (1) {
result = smb_receive(server);
if (result < 0)
result = -EIO;
sock = server_sock(server);
if (!sock)
goto out;
inbuf = server->packet;
if (server->rcls != 0) {
*parm = *data = inbuf;
*ldata = *lparm = 0;
if (sock->sk->state != TCP_ESTABLISHED)
goto out;
}
/*
* Extract the control data from the packet.
*/
data_tot = WVAL(inbuf, smb_tdrcnt);
parm_tot = WVAL(inbuf, smb_tprcnt);
parm_disp = WVAL(inbuf, smb_prdisp);
parm_offset = WVAL(inbuf, smb_proff);
parm_count = WVAL(inbuf, smb_prcnt);
data_disp = WVAL(inbuf, smb_drdisp);
data_offset = WVAL(inbuf, smb_droff);
data_count = WVAL(inbuf, smb_drcnt);
base = smb_base(inbuf);
/*
* Assume success and increment lengths.
*/
parm_len += parm_count;
data_len += data_count;
if (!rcv_buf) {
/*
* Check for fast track processing ... just this packet.
*/
if (parm_count == parm_tot && data_count == data_tot) {
VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n",
parm_disp, parm_offset, parm_count,
data_disp, data_offset, data_count);
*parm = base + parm_offset;
*data = base + data_offset;
goto success;
if (!server->smb_read) {
result = smb_get_length(sock, peek_buf);
if (result < 0) {
if (result == -ENODATA)
result = 0;
goto out;
}
server->smb_len = result + 4;
/*
* Save the total parameter and data length.
*/
total_d = data_tot;
total_p = parm_tot;
buf_len = total_d + total_p;
if (server->packet_size > buf_len)
buf_len = server->packet_size;
buf_len = smb_round_length(buf_len);
if (buf_len > SMB_MAX_PACKET_SIZE)
goto out_too_long;
rcv_buf = smb_vmalloc(buf_len);
if (!rcv_buf)
goto out_no_mem;
*parm = rcv_buf;
*data = rcv_buf + total_p;
} else if (data_tot > total_d || parm_tot > total_p)
goto out_data_grew;
if (parm_disp + parm_count > total_p)
goto out_bad_parm;
if (data_disp + data_count > total_d)
goto out_bad_data;
memcpy(*parm + parm_disp, base + parm_offset, parm_count);
memcpy(*data + data_disp, base + data_offset, data_count);
PARANOIA("copied, parm=%u of %u, data=%u of %u\n",
parm_len, parm_tot, data_len, data_tot);
/*
* Check whether we've received all of the data. Note that
* we use the packet totals -- total lengths might shrink!
*/
if (data_len >= data_tot && parm_len >= parm_tot)
break;
if (server->smb_len < SMB_HEADER_LEN) {
PARANOIA("short packet: %d\n", result);
server->rstate = SMB_RECV_DROP;
result = -EIO;
goto out;
}
if (server->smb_len > SMB_MAX_PACKET_SIZE) {
PARANOIA("long packet: %d\n", result);
server->rstate = SMB_RECV_DROP;
result = -EIO;
goto out;
}
}
/*
* Install the new packet. Note that it's possible, though
* unlikely, that the new packet could be smaller than the
* old one, in which case we just copy the data.
*/
inbuf = server->packet;
if (buf_len >= server->packet_size) {
server->packet_size = buf_len;
server->packet = rcv_buf;
rcv_buf = inbuf;
} else {
PARANOIA("copying data, old size=%d, new size=%u\n",
server->packet_size, buf_len);
memcpy(inbuf, rcv_buf, parm_len + data_len);
result = _recvfrom(sock, server->header + server->smb_read,
SMB_HEADER_LEN - server->smb_read, 0);
VERBOSE("_recvfrom: %d\n", result);
if (result < 0) {
VERBOSE("receive error: %d\n", result);
goto out;
}
server->smb_read += result;
success:
*ldata = data_len;
*lparm = parm_len;
if (server->smb_read == SMB_HEADER_LEN)
server->rstate = SMB_RECV_HCOMPLETE;
out:
if (rcv_buf)
smb_vfree(rcv_buf);
return result;
out_no_mem:
PARANOIA("couldn't allocate data area\n");
result = -ENOMEM;
goto out;
out_too_long:
printk(KERN_ERR "smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
data_tot, parm_tot);
goto out_error;
out_data_grew:
printk(KERN_ERR "smb_receive_trans2: data/params grew!\n");
goto out_error;
out_bad_parm:
printk(KERN_ERR "smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
parm_disp, parm_count, parm_tot);
goto out_error;
out_bad_data:
printk(KERN_ERR "smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
data_disp, data_count, data_tot);
out_error:
result = -EIO;
goto out;
}
static char drop_buffer[PAGE_SIZE];
/*
* Called with the server locked
* smb_receive_drop - read and throw away the data
* Only called by the smbiod thread.
*
* FIXME: we are in the kernel, could we just tell the socket that we want
* to drop stuff from the buffer?
*/
int
smb_request(struct smb_sb_info *server)
smb_receive_drop(struct smb_sb_info *server)
{
unsigned long flags, sigpipe;
struct socket *sock;
unsigned int flags;
struct iovec iov;
struct msghdr msg;
struct scm_cookie scm;
mm_segment_t fs;
sigset_t old_set;
int len, result;
unsigned char *buffer;
result = -EBADF;
buffer = server->packet;
if (!buffer)
goto bad_no_packet;
result = -EIO;
if (server->state != CONN_VALID)
goto bad_no_conn;
if ((result = smb_dont_catch_keepalive(server)) != 0)
goto bad_conn;
len = smb_len(buffer) + 4;
DEBUG1("len = %d cmd = 0x%X\n", len, buffer[8]);
int rlen = smb_len(server->header) - server->smb_read + 4;
int result = -EIO;
spin_lock_irqsave(&current->sigmask_lock, flags);
sigpipe = sigismember(&current->pending.signal, SIGPIPE);
old_set = current->blocked;
siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
recalc_sigpending();
spin_unlock_irqrestore(&current->sigmask_lock, flags);
sock = server_sock(server);
if (!sock)
goto out;
if (sock->sk->state != TCP_ESTABLISHED)
goto out;
fs = get_fs();
set_fs(get_ds());
result = smb_send_raw(server_sock(server), (void *) buffer, len);
if (result > 0)
{
result = smb_receive(server);
}
flags = MSG_DONTWAIT | MSG_NOSIGNAL;
iov.iov_base = drop_buffer;
iov.iov_len = PAGE_SIZE;
msg.msg_flags = flags;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
/* read/write errors are handled by errno */
spin_lock_irqsave(&current->sigmask_lock, flags);
if (result == -EPIPE && !sigpipe)
sigdelset(&current->pending.signal, SIGPIPE);
current->blocked = old_set;
recalc_sigpending();
spin_unlock_irqrestore(&current->sigmask_lock, flags);
if (rlen > PAGE_SIZE)
rlen = PAGE_SIZE;
memset(&scm, 0, sizeof(scm));
result = sock->ops->recvmsg(sock, &msg, rlen, flags, &scm);
if (result >= 0)
scm_recv(sock, &msg, &scm, flags);
set_fs(fs);
if (result >= 0)
{
int result2 = smb_catch_keepalive(server);
if (result2 < 0)
{
printk(KERN_ERR "smb_request: catch keepalive failed\n");
result = result2;
}
}
if (result < 0)
goto bad_conn;
/*
* Check for fatal server errors ...
*/
if (server->rcls) {
int error = smb_errno(server);
if (error == -EBADSLT) {
printk(KERN_ERR "smb_request: tree ID invalid\n");
result = error;
goto bad_conn;
}
VERBOSE("read: %d\n", result);
if (result < 0) {
VERBOSE("receive error: %d\n", result);
goto out;
}
server->smb_read += result;
if (server->smb_read >= server->smb_len)
server->rstate = SMB_RECV_END;
out:
DEBUG1("result = %d\n", result);
return result;
bad_conn:
PARANOIA("result %d, setting invalid\n", result);
server->state = CONN_INVALID;
smb_invalidate_inodes(server);
goto out;
bad_no_packet:
printk(KERN_ERR "smb_request: no packet!\n");
goto out;
bad_no_conn:
printk(KERN_ERR "smb_request: connection %d not valid!\n",
server->state);
goto out;
}
#define ROUND_UP(x) (((x)+3) & ~3)
static int
smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
int ldata, unsigned char *data,
int lparam, unsigned char *param)
/*
* smb_receive
* Only called by the smbiod thread.
*/
int
smb_receive(struct smb_sb_info *server, struct smb_request *req)
{
struct socket *sock = server_sock(server);
struct scm_cookie scm;
int err;
int mparam, mdata;
/* 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 socket *sock;
unsigned int flags;
struct iovec iov[4];
struct msghdr msg;
struct scm_cookie scm;
mm_segment_t fs;
int rlen;
int result = -EIO;
/* FIXME! this test needs to include SMB overhead too, I think ... */
if ((bcc + oparam) > server->opt.max_xmit)
return -ENOMEM;
p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
/*
* max parameters + max data + max setup == max_xmit to make NT4 happy
* and not abort the transfer or split into multiple responses.
*
* -100 is to make room for headers, which OS/2 seems to include in the
* size calculation while NT4 does not?
*/
mparam = SMB_TRANS2_MAX_PARAM;
mdata = server->opt.max_xmit - mparam - 100;
if (mdata < 1024) {
mdata = 1024;
mparam = 20;
}
WSET(server->packet, smb_tpscnt, lparam);
WSET(server->packet, smb_tdscnt, ldata);
WSET(server->packet, smb_mprcnt, mparam);
WSET(server->packet, smb_mdrcnt, mdata);
WSET(server->packet, smb_msrcnt, 0); /* max setup always 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++ = ' ';
sock = server_sock(server);
if (!sock)
goto out;
if (sock->sk->state != TCP_ESTABLISHED)
goto out;
fs = get_fs();
set_fs(get_ds());
flags = MSG_DONTWAIT | MSG_NOSIGNAL;;
msg.msg_flags = flags;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = req->rq_iov;
msg.msg_iovlen = req->rq_iovlen;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 4;
msg.msg_flags = 0;
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;
err = scm_send(sock, &msg, &scm);
if (err >= 0) {
err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
scm_destroy(&scm);
/* Dont repeat bytes and count available bufferspace */
rlen = smb_move_iov(&msg, iov, req->rq_bytes_recvd);
if (req->rq_rlen < rlen)
rlen = req->rq_rlen;
memset(&scm, 0, sizeof(scm));
result = sock->ops->recvmsg(sock, &msg, rlen, flags, &scm);
if (result >= 0)
scm_recv(sock, &msg, &scm, flags);
set_fs(fs);
VERBOSE("read: %d\n", result);
if (result < 0) {
VERBOSE("receive error: %d\n", result);
goto out;
}
return err;
req->rq_bytes_recvd += result;
server->smb_read += result;
out:
return result;
}
/*
* This is not really a trans2 request, we assume that you only have
* one packet to send.
* Try to send a SMB request. This may return after sending only parts of the
* request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
*
* Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
*/
int
smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
int ldata, unsigned char *data,
int lparam, unsigned char *param,
int *lrdata, unsigned char **rdata,
int *lrparam, unsigned char **rparam)
smb_send_request(struct smb_request *req)
{
sigset_t old_set;
unsigned long flags, sigpipe;
mm_segment_t fs;
int result;
DEBUG1("com=%d, ld=%d, lp=%d\n", trans2_command, ldata, lparam);
/*
* These are initialized in smb_request_ok, but not here??
*/
server->rcls = 0;
server->err = 0;
struct smb_sb_info *server = req->rq_server;
struct socket *sock;
struct scm_cookie scm;
struct msghdr msg;
int slen = req->rq_slen - req->rq_bytes_sent;
int result = -EIO;
struct iovec iov[4];
result = -EIO;
if (server->state != CONN_VALID)
sock = server_sock(server);
if (!sock)
goto out;
if (sock->sk->state != TCP_ESTABLISHED)
goto out;
if ((result = smb_dont_catch_keepalive(server)) != 0)
goto bad_conn;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = req->rq_iov;
msg.msg_iovlen = req->rq_iovlen;
msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
spin_lock_irqsave(&current->sigmask_lock, flags);
sigpipe = sigismember(&current->pending.signal, SIGPIPE);
old_set = current->blocked;
siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
recalc_sigpending();
spin_unlock_irqrestore(&current->sigmask_lock, flags);
/* Dont repeat bytes */
if (req->rq_bytes_sent)
smb_move_iov(&msg, iov, req->rq_bytes_sent);
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);
result = scm_send(sock, &msg, &scm);
if (result >= 0) {
result = sock->ops->sendmsg(sock, &msg, slen, &scm);
scm_destroy(&scm);
}
/* read/write errors are handled by errno */
spin_lock_irqsave(&current->sigmask_lock, flags);
if (result == -EPIPE && !sigpipe)
sigdelset(&current->pending.signal, SIGPIPE);
current->blocked = old_set;
recalc_sigpending();
spin_unlock_irqrestore(&current->sigmask_lock, flags);
set_fs(fs);
if (result >= 0)
{
int result2 = smb_catch_keepalive(server);
if (result2 < 0)
{
result = result2;
}
}
if (result < 0)
goto bad_conn;
/*
* Check for fatal server errors ...
*/
if (server->rcls) {
int error = smb_errno(server);
if (error == -EBADSLT) {
printk(KERN_ERR "smb_request: tree ID invalid\n");
result = error;
goto bad_conn;
if (result >= 0) {
req->rq_bytes_sent += result;
if (req->rq_bytes_sent >= req->rq_slen)
req->rq_flags |= SMB_REQ_TRANSMITTED;
}
}
out:
return result;
bad_conn:
PARANOIA("result=%d, setting invalid\n", result);
server->state = CONN_INVALID;
smb_invalidate_inodes(server);
goto out;
}
......@@ -14,17 +14,42 @@
#include <linux/types.h>
#include <linux/smb.h>
/*
* Upper limit on the total number of active smb_request structs.
*/
#define MAX_REQUEST_HARD 256
enum smb_receive_state {
SMB_RECV_START, /* No data read, looking for length + sig */
SMB_RECV_HEADER, /* Reading the header data */
SMB_RECV_HCOMPLETE, /* Done with the header */
SMB_RECV_PARAM, /* Reading parameter words */
SMB_RECV_DATA, /* Reading data bytes */
SMB_RECV_END, /* End of request */
SMB_RECV_DROP, /* Dropping this SMB */
SMB_RECV_REQUEST, /* Received a request and not a reply */
};
/* structure access macros */
#define server_from_inode(inode) SMB_SB((inode)->i_sb)
#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb)
#define SB_of(server) ((server)->super_block)
struct smb_sb_info {
/* List of all smbfs superblocks */
struct list_head entry;
enum smb_conn_state state;
struct file * sock_file;
int conn_error;
enum smb_receive_state rstate;
atomic_t nr_requests;
struct list_head xmitq;
struct list_head recvq;
u16 mid;
struct smb_mount_data_kernel *mnt;
unsigned char *temp_buf;
/* Connections are counted. Each time a new socket arrives,
* generation is incremented.
......@@ -34,13 +59,15 @@ struct smb_sb_info {
struct smb_conn_opt opt;
struct semaphore sem;
wait_queue_head_t wait;
__u32 packet_size;
unsigned char * packet;
unsigned short rcls; /* The error codes we received */
unsigned short err;
unsigned char header[SMB_HEADER_LEN + 20*2 + 2];
u32 header_len;
u32 smb_len;
u32 smb_read;
/* We use our own data_ready callback, but need the original one */
void *data_ready;
......@@ -48,15 +75,16 @@ struct smb_sb_info {
struct nls_table *remote_nls;
struct nls_table *local_nls;
/* utf8 can make strings longer so we can't do in-place conversion.
This is a buffer for temporary stuff. We only need one so no need
to put it on the stack. This points to temp_buf space. */
char *name_buf;
struct smb_ops *ops;
struct super_block *super_block;
};
static inline int
smb_lock_server_interruptible(struct smb_sb_info *server)
{
return down_interruptible(&(server->sem));
}
static inline void
smb_lock_server(struct smb_sb_info *server)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment