/*
|
/*
|
* proc.c
|
* proc.c
|
*
|
*
|
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
|
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
|
*
|
*
|
* 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
|
* 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
|
*/
|
*/
|
|
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/fs.h>
|
#include <linux/fs.h>
|
#include <linux/smbno.h>
|
#include <linux/smbno.h>
|
#include <linux/smb_fs.h>
|
#include <linux/smb_fs.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <linux/fcntl.h>
|
#include <linux/fcntl.h>
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
#include <asm/string.h>
|
#include <asm/string.h>
|
|
|
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
|
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
|
#define SMB_CMD(packet) (BVAL(packet,8))
|
#define SMB_CMD(packet) (BVAL(packet,8))
|
#define SMB_WCT(packet) (BVAL(packet, SMB_HEADER_LEN - 1))
|
#define SMB_WCT(packet) (BVAL(packet, SMB_HEADER_LEN - 1))
|
#define SMB_BCC(packet) smb_bcc(packet)
|
#define SMB_BCC(packet) smb_bcc(packet)
|
#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
|
#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
|
|
|
#define SMB_DIRINFO_SIZE 43
|
#define SMB_DIRINFO_SIZE 43
|
#define SMB_STATUS_SIZE 21
|
#define SMB_STATUS_SIZE 21
|
|
|
static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
|
static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
|
|
|
static inline int
|
static inline int
|
min(int a, int b)
|
min(int a, int b)
|
{
|
{
|
return a < b ? a : b;
|
return a < b ? a : b;
|
}
|
}
|
|
|
static void
|
static void
|
str_upper(char *name)
|
str_upper(char *name)
|
{
|
{
|
while (*name)
|
while (*name)
|
{
|
{
|
if (*name >= 'a' && *name <= 'z')
|
if (*name >= 'a' && *name <= 'z')
|
*name -= ('a' - 'A');
|
*name -= ('a' - 'A');
|
name++;
|
name++;
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
str_lower(char *name)
|
str_lower(char *name)
|
{
|
{
|
while (*name)
|
while (*name)
|
{
|
{
|
if (*name >= 'A' && *name <= 'Z')
|
if (*name >= 'A' && *name <= 'Z')
|
*name += ('a' - 'A');
|
*name += ('a' - 'A');
|
name++;
|
name++;
|
}
|
}
|
}
|
}
|
|
|
/*****************************************************************************/
|
/*****************************************************************************/
|
/* */
|
/* */
|
/* Encoding/Decoding section */
|
/* Encoding/Decoding section */
|
/* */
|
/* */
|
/*****************************************************************************/
|
/*****************************************************************************/
|
|
|
static inline byte *
|
static inline byte *
|
smb_decode_word(byte * p, word * data)
|
smb_decode_word(byte * p, word * data)
|
{
|
{
|
*data = WVAL(p, 0);
|
*data = WVAL(p, 0);
|
return p + 2;
|
return p + 2;
|
}
|
}
|
|
|
byte *
|
byte *
|
smb_encode_smb_length(byte * p, dword len)
|
smb_encode_smb_length(byte * p, dword len)
|
{
|
{
|
BSET(p, 0, 0);
|
BSET(p, 0, 0);
|
BSET(p, 1, 0);
|
BSET(p, 1, 0);
|
BSET(p, 2, (len & 0xFF00) >> 8);
|
BSET(p, 2, (len & 0xFF00) >> 8);
|
BSET(p, 3, (len & 0xFF));
|
BSET(p, 3, (len & 0xFF));
|
if (len > 0xFFFF)
|
if (len > 0xFFFF)
|
{
|
{
|
BSET(p, 1, 1);
|
BSET(p, 1, 1);
|
}
|
}
|
return p + 4;
|
return p + 4;
|
}
|
}
|
|
|
static byte *
|
static byte *
|
smb_encode_ascii(byte * p, const byte * name, int len)
|
smb_encode_ascii(byte * p, const byte * name, int len)
|
{
|
{
|
*p++ = 4;
|
*p++ = 4;
|
strcpy(p, name);
|
strcpy(p, name);
|
return p + len + 1;
|
return p + len + 1;
|
}
|
}
|
|
|
static byte *
|
static byte *
|
smb_encode_this_name(byte * p, const char *name, const int len)
|
smb_encode_this_name(byte * p, const char *name, const int len)
|
{
|
{
|
*p++ = '\\';
|
*p++ = '\\';
|
strncpy(p, name, len);
|
strncpy(p, name, len);
|
return p + len;
|
return p + len;
|
}
|
}
|
|
|
/* I put smb_encode_parents into a separate function so that the
|
/* I put smb_encode_parents into a separate function so that the
|
recursion only takes 16 bytes on the stack per path component on a
|
recursion only takes 16 bytes on the stack per path component on a
|
386. */
|
386. */
|
|
|
static byte *
|
static byte *
|
smb_encode_parents(byte * p, struct smb_inode_info *ino)
|
smb_encode_parents(byte * p, struct smb_inode_info *ino)
|
{
|
{
|
byte *q;
|
byte *q;
|
|
|
if (ino->dir == NULL)
|
if (ino->dir == NULL)
|
{
|
{
|
return p;
|
return p;
|
}
|
}
|
q = smb_encode_parents(p, ino->dir);
|
q = smb_encode_parents(p, ino->dir);
|
if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN)
|
if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN)
|
{
|
{
|
return p;
|
return p;
|
}
|
}
|
return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);
|
return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);
|
}
|
}
|
|
|
static byte *
|
static byte *
|
smb_encode_path(struct smb_server *server,
|
smb_encode_path(struct smb_server *server,
|
byte * p, struct smb_inode_info *dir,
|
byte * p, struct smb_inode_info *dir,
|
const char *name, const int len)
|
const char *name, const int len)
|
{
|
{
|
byte *start = p;
|
byte *start = p;
|
if (dir != NULL)
|
if (dir != NULL)
|
{
|
{
|
p = smb_encode_parents(p, dir);
|
p = smb_encode_parents(p, dir);
|
}
|
}
|
p = smb_encode_this_name(p, name, len);
|
p = smb_encode_this_name(p, name, len);
|
*p++ = 0;
|
*p++ = 0;
|
if (server->protocol <= PROTOCOL_COREPLUS)
|
if (server->protocol <= PROTOCOL_COREPLUS)
|
{
|
{
|
str_upper(start);
|
str_upper(start);
|
}
|
}
|
return p;
|
return p;
|
}
|
}
|
|
|
static byte *
|
static byte *
|
smb_decode_data(byte * p, byte * data, word * data_len, int fs)
|
smb_decode_data(byte * p, byte * data, word * data_len, int fs)
|
{
|
{
|
word len;
|
word len;
|
|
|
if (!(*p == 1 || *p == 5))
|
if (!(*p == 1 || *p == 5))
|
{
|
{
|
printk("smb_decode_data: Warning! Data block not starting "
|
printk("smb_decode_data: Warning! Data block not starting "
|
"with 1 or 5\n");
|
"with 1 or 5\n");
|
}
|
}
|
len = WVAL(p, 1);
|
len = WVAL(p, 1);
|
p += 3;
|
p += 3;
|
|
|
if (fs)
|
if (fs)
|
memcpy_tofs(data, p, len);
|
memcpy_tofs(data, p, len);
|
else
|
else
|
memcpy(data, p, len);
|
memcpy(data, p, len);
|
|
|
*data_len = len;
|
*data_len = len;
|
|
|
return p + len;
|
return p + len;
|
}
|
}
|
|
|
static byte *
|
static byte *
|
smb_name_mangle(byte * p, const byte * name)
|
smb_name_mangle(byte * p, const byte * name)
|
{
|
{
|
int len, pad = 0;
|
int len, pad = 0;
|
|
|
len = strlen(name);
|
len = strlen(name);
|
|
|
if (len < 16)
|
if (len < 16)
|
pad = 16 - len;
|
pad = 16 - len;
|
|
|
*p++ = 2 * (len + pad);
|
*p++ = 2 * (len + pad);
|
|
|
while (*name)
|
while (*name)
|
{
|
{
|
*p++ = (*name >> 4) + 'A';
|
*p++ = (*name >> 4) + 'A';
|
*p++ = (*name & 0x0F) + 'A';
|
*p++ = (*name & 0x0F) + 'A';
|
name++;
|
name++;
|
}
|
}
|
while (pad--)
|
while (pad--)
|
{
|
{
|
*p++ = 'C';
|
*p++ = 'C';
|
*p++ = 'A';
|
*p++ = 'A';
|
}
|
}
|
*p++ = '\0';
|
*p++ = '\0';
|
|
|
return p;
|
return p;
|
}
|
}
|
|
|
/* The following are taken directly from msdos-fs */
|
/* The following are taken directly from msdos-fs */
|
|
|
/* Linear day numbers of the respective 1sts in non-leap years. */
|
/* Linear day numbers of the respective 1sts in non-leap years. */
|
|
|
static int day_n[] =
|
static int day_n[] =
|
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
|
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
|
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
|
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
|
|
|
|
|
extern struct timezone sys_tz;
|
extern struct timezone sys_tz;
|
|
|
static int
|
static int
|
utc2local(int time)
|
utc2local(int time)
|
{
|
{
|
return time - sys_tz.tz_minuteswest * 60 + sys_tz.tz_dsttime * 3600;
|
return time - sys_tz.tz_minuteswest * 60 + sys_tz.tz_dsttime * 3600;
|
}
|
}
|
|
|
static int
|
static int
|
local2utc(int time)
|
local2utc(int time)
|
{
|
{
|
return time + sys_tz.tz_minuteswest * 60 - sys_tz.tz_dsttime * 3600;
|
return time + sys_tz.tz_minuteswest * 60 - sys_tz.tz_dsttime * 3600;
|
}
|
}
|
|
|
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
|
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
|
|
|
static int
|
static int
|
date_dos2unix(unsigned short time, unsigned short date)
|
date_dos2unix(unsigned short time, unsigned short date)
|
{
|
{
|
int month, year, secs;
|
int month, year, secs;
|
|
|
month = ((date >> 5) & 15) - 1;
|
month = ((date >> 5) & 15) - 1;
|
year = date >> 9;
|
year = date >> 9;
|
secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
|
secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
|
((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
|
((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
|
month < 2 ? 1 : 0) + 3653);
|
month < 2 ? 1 : 0) + 3653);
|
/* days since 1.1.70 plus 80's leap day */
|
/* days since 1.1.70 plus 80's leap day */
|
return local2utc(secs);
|
return local2utc(secs);
|
}
|
}
|
|
|
/*****************************************************************************/
|
/*****************************************************************************/
|
/* */
|
/* */
|
/* Support section. */
|
/* Support section. */
|
/* */
|
/* */
|
/*****************************************************************************/
|
/*****************************************************************************/
|
|
|
dword
|
dword
|
smb_len(byte * p)
|
smb_len(byte * p)
|
{
|
{
|
return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));
|
return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));
|
}
|
}
|
|
|
static word
|
static word
|
smb_bcc(byte * packet)
|
smb_bcc(byte * packet)
|
{
|
{
|
int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
|
int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
|
return WVAL(packet, pos);
|
return WVAL(packet, pos);
|
}
|
}
|
|
|
/* smb_valid_packet: We check if packet fulfills the basic
|
/* smb_valid_packet: We check if packet fulfills the basic
|
requirements of a smb packet */
|
requirements of a smb packet */
|
|
|
static int
|
static int
|
smb_valid_packet(byte * packet)
|
smb_valid_packet(byte * packet)
|
{
|
{
|
DDPRINTK("len: %d, wct: %d, bcc: %d\n",
|
DDPRINTK("len: %d, wct: %d, bcc: %d\n",
|
smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
|
smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
|
return (packet[4] == 0xff
|
return (packet[4] == 0xff
|
&& packet[5] == 'S'
|
&& packet[5] == 'S'
|
&& packet[6] == 'M'
|
&& packet[6] == 'M'
|
&& packet[7] == 'B'
|
&& packet[7] == 'B'
|
&& (smb_len(packet) + 4 == SMB_HEADER_LEN
|
&& (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
|
/* smb_verify: We check if we got the answer we expected, and if we
|
got enough data. If bcc == -1, we don't care. */
|
got enough data. If bcc == -1, we don't care. */
|
|
|
static int
|
static int
|
smb_verify(byte * packet, int command, int wct, int bcc)
|
smb_verify(byte * packet, int command, int wct, int bcc)
|
{
|
{
|
return (SMB_CMD(packet) == command &&
|
return (SMB_CMD(packet) == command &&
|
SMB_WCT(packet) >= wct &&
|
SMB_WCT(packet) >= wct &&
|
(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
|
(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
|
}
|
}
|
|
|
static int
|
static int
|
smb_errno(int errcls, int error)
|
smb_errno(int errcls, int error)
|
{
|
{
|
if (errcls == ERRDOS)
|
if (errcls == ERRDOS)
|
switch (error)
|
switch (error)
|
{
|
{
|
case ERRbadfunc:
|
case ERRbadfunc:
|
return EINVAL;
|
return EINVAL;
|
case ERRbadfile:
|
case ERRbadfile:
|
return ENOENT;
|
return ENOENT;
|
case ERRbadpath:
|
case ERRbadpath:
|
return ENOENT;
|
return ENOENT;
|
case ERRnofids:
|
case ERRnofids:
|
return EMFILE;
|
return EMFILE;
|
case ERRnoaccess:
|
case ERRnoaccess:
|
return EACCES;
|
return EACCES;
|
case ERRbadfid:
|
case ERRbadfid:
|
return EBADF;
|
return EBADF;
|
case ERRbadmcb:
|
case ERRbadmcb:
|
return EREMOTEIO;
|
return EREMOTEIO;
|
case ERRnomem:
|
case ERRnomem:
|
return ENOMEM;
|
return ENOMEM;
|
case ERRbadmem:
|
case ERRbadmem:
|
return EFAULT;
|
return EFAULT;
|
case ERRbadenv:
|
case ERRbadenv:
|
return EREMOTEIO;
|
return EREMOTEIO;
|
case ERRbadformat:
|
case ERRbadformat:
|
return EREMOTEIO;
|
return EREMOTEIO;
|
case ERRbadaccess:
|
case ERRbadaccess:
|
return EACCES;
|
return EACCES;
|
case ERRbaddata:
|
case ERRbaddata:
|
return E2BIG;
|
return E2BIG;
|
case ERRbaddrive:
|
case ERRbaddrive:
|
return ENXIO;
|
return ENXIO;
|
case ERRremcd:
|
case ERRremcd:
|
return EREMOTEIO;
|
return EREMOTEIO;
|
case ERRdiffdevice:
|
case ERRdiffdevice:
|
return EXDEV;
|
return EXDEV;
|
case ERRnofiles:
|
case ERRnofiles:
|
return 0;
|
return 0;
|
case ERRbadshare:
|
case ERRbadshare:
|
return ETXTBSY;
|
return ETXTBSY;
|
case ERRlock:
|
case ERRlock:
|
return EDEADLK;
|
return EDEADLK;
|
case ERRfilexists:
|
case ERRfilexists:
|
return EEXIST;
|
return EEXIST;
|
case 87:
|
case 87:
|
return 0; /* Unknown error!! */
|
return 0; /* Unknown error!! */
|
/* This next error seems to occur on an mv when
|
/* This next error seems to occur on an mv when
|
* the destination exists */
|
* the destination exists */
|
case 183:
|
case 183:
|
return EEXIST;
|
return EEXIST;
|
default:
|
default:
|
return EIO;
|
return EIO;
|
} else if (errcls == ERRSRV)
|
} else if (errcls == ERRSRV)
|
switch (error)
|
switch (error)
|
{
|
{
|
case ERRerror:
|
case ERRerror:
|
return ENFILE;
|
return ENFILE;
|
case ERRbadpw:
|
case ERRbadpw:
|
return EINVAL;
|
return EINVAL;
|
case ERRbadtype:
|
case ERRbadtype:
|
return EIO;
|
return EIO;
|
case ERRaccess:
|
case ERRaccess:
|
return EACCES;
|
return EACCES;
|
default:
|
default:
|
return EIO;
|
return EIO;
|
} else if (errcls == ERRHRD)
|
} else if (errcls == ERRHRD)
|
switch (error)
|
switch (error)
|
{
|
{
|
case ERRnowrite:
|
case ERRnowrite:
|
return EROFS;
|
return EROFS;
|
case ERRbadunit:
|
case ERRbadunit:
|
return ENODEV;
|
return ENODEV;
|
case ERRnotready:
|
case ERRnotready:
|
return EUCLEAN;
|
return EUCLEAN;
|
case ERRbadcmd:
|
case ERRbadcmd:
|
return EIO;
|
return EIO;
|
case ERRdata:
|
case ERRdata:
|
return EIO;
|
return EIO;
|
case ERRbadreq:
|
case ERRbadreq:
|
return ERANGE;
|
return ERANGE;
|
case ERRbadshare:
|
case ERRbadshare:
|
return ETXTBSY;
|
return ETXTBSY;
|
case ERRlock:
|
case ERRlock:
|
return EDEADLK;
|
return EDEADLK;
|
default:
|
default:
|
return EIO;
|
return EIO;
|
} else if (errcls == ERRCMD)
|
} else if (errcls == ERRCMD)
|
return EIO;
|
return EIO;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static void
|
static void
|
smb_lock_server(struct smb_server *server)
|
smb_lock_server(struct smb_server *server)
|
{
|
{
|
while (server->lock)
|
while (server->lock)
|
sleep_on(&server->wait);
|
sleep_on(&server->wait);
|
server->lock = 1;
|
server->lock = 1;
|
}
|
}
|
|
|
static void
|
static void
|
smb_unlock_server(struct smb_server *server)
|
smb_unlock_server(struct smb_server *server)
|
{
|
{
|
if (server->lock != 1)
|
if (server->lock != 1)
|
{
|
{
|
printk("smb_unlock_server: was not locked!\n");
|
printk("smb_unlock_server: was not locked!\n");
|
}
|
}
|
server->lock = 0;
|
server->lock = 0;
|
wake_up(&server->wait);
|
wake_up(&server->wait);
|
}
|
}
|
|
|
/* smb_request_ok: We expect the server to be locked. Then we do the
|
/* smb_request_ok: We expect the server to be locked. Then we do the
|
request and check the answer completely. When smb_request_ok
|
request and check the answer completely. When smb_request_ok
|
returns 0, you can be quite sure that everything went well. When
|
returns 0, you can be quite sure that everything went well. When
|
the answer is <=0, the returned number is a valid unix errno. */
|
the answer is <=0, the returned number is a valid unix errno. */
|
|
|
static int
|
static int
|
smb_request_ok(struct smb_server *s, int command, int wct, int bcc)
|
smb_request_ok(struct smb_server *s, int command, int wct, int bcc)
|
{
|
{
|
int result = 0;
|
int result = 0;
|
s->rcls = 0;
|
s->rcls = 0;
|
s->err = 0;
|
s->err = 0;
|
|
|
if (smb_request(s) < 0)
|
if (smb_request(s) < 0)
|
{
|
{
|
DPRINTK("smb_request failed\n");
|
DPRINTK("smb_request failed\n");
|
result = -EIO;
|
result = -EIO;
|
} else if (smb_valid_packet(s->packet) != 0)
|
} else if (smb_valid_packet(s->packet) != 0)
|
{
|
{
|
DPRINTK("not a valid packet!\n");
|
DPRINTK("not a valid packet!\n");
|
result = -EIO;
|
result = -EIO;
|
} else if (s->rcls != 0)
|
} else if (s->rcls != 0)
|
{
|
{
|
result = -smb_errno(s->rcls, s->err);
|
result = -smb_errno(s->rcls, s->err);
|
} else if (smb_verify(s->packet, command, wct, bcc) != 0)
|
} else if (smb_verify(s->packet, command, wct, bcc) != 0)
|
{
|
{
|
DPRINTK("smb_verify failed\n");
|
DPRINTK("smb_verify failed\n");
|
result = -EIO;
|
result = -EIO;
|
}
|
}
|
return result;
|
return result;
|
}
|
}
|
|
|
/* smb_retry: This function should be called when smb_request_ok has
|
/* smb_retry: This function should be called when smb_request_ok has
|
indicated an error. If the error was indicated because the
|
indicated an error. If the error was indicated because the
|
connection was killed, we try to reconnect. If smb_retry returns 0,
|
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
|
the error was indicated for another reason, so a retry would not be
|
of any use. */
|
of any use. */
|
|
|
static int
|
static int
|
smb_retry(struct smb_server *server)
|
smb_retry(struct smb_server *server)
|
{
|
{
|
if (server->state != CONN_INVALID)
|
if (server->state != CONN_INVALID)
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
if (smb_release(server) < 0)
|
if (smb_release(server) < 0)
|
{
|
{
|
DPRINTK("smb_retry: smb_release failed\n");
|
DPRINTK("smb_retry: smb_release failed\n");
|
server->state = CONN_RETRIED;
|
server->state = CONN_RETRIED;
|
return 0;
|
return 0;
|
}
|
}
|
if (smb_proc_reconnect(server) < 0)
|
if (smb_proc_reconnect(server) < 0)
|
{
|
{
|
DPRINTK("smb_proc_reconnect failed\n");
|
DPRINTK("smb_proc_reconnect failed\n");
|
server->state = CONN_RETRIED;
|
server->state = CONN_RETRIED;
|
return 0;
|
return 0;
|
}
|
}
|
server->state = CONN_VALID;
|
server->state = CONN_VALID;
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static int
|
static int
|
smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
|
smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
|
{
|
{
|
int result = smb_request_ok(s, command, wct, bcc);
|
int result = smb_request_ok(s, command, wct, bcc);
|
|
|
smb_unlock_server(s);
|
smb_unlock_server(s);
|
|
|
return result;
|
return result;
|
}
|
}
|
|
|
/* smb_setup_header: We completely set up the packet. You only have to
|
/* smb_setup_header: We completely set up the packet. You only have to
|
insert the command-specific fields */
|
insert the command-specific fields */
|
|
|
__u8 *
|
__u8 *
|
smb_setup_header(struct smb_server * server, byte command, word wct, word bcc)
|
smb_setup_header(struct smb_server * server, byte command, word wct, word bcc)
|
{
|
{
|
dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
|
dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
|
byte *p = server->packet;
|
byte *p = server->packet;
|
byte *buf = server->packet;
|
byte *buf = server->packet;
|
|
|
p = smb_encode_smb_length(p, xmit_len - 4);
|
p = smb_encode_smb_length(p, xmit_len - 4);
|
|
|
BSET(p, 0, 0xff);
|
BSET(p, 0, 0xff);
|
BSET(p, 1, 'S');
|
BSET(p, 1, 'S');
|
BSET(p, 2, 'M');
|
BSET(p, 2, 'M');
|
BSET(p, 3, 'B');
|
BSET(p, 3, 'B');
|
BSET(p, 4, command);
|
BSET(p, 4, command);
|
|
|
p += 5;
|
p += 5;
|
memset(p, '\0', 19);
|
memset(p, '\0', 19);
|
p += 19;
|
p += 19;
|
p += 8;
|
p += 8;
|
|
|
WSET(buf, smb_tid, server->tid);
|
WSET(buf, smb_tid, server->tid);
|
WSET(buf, smb_pid, server->pid);
|
WSET(buf, smb_pid, server->pid);
|
WSET(buf, smb_uid, server->server_uid);
|
WSET(buf, smb_uid, server->server_uid);
|
WSET(buf, smb_mid, server->mid);
|
WSET(buf, smb_mid, server->mid);
|
|
|
if (server->protocol > PROTOCOL_CORE)
|
if (server->protocol > PROTOCOL_CORE)
|
{
|
{
|
BSET(buf, smb_flg, 0x8);
|
BSET(buf, smb_flg, 0x8);
|
WSET(buf, smb_flg2, 0x3);
|
WSET(buf, smb_flg2, 0x3);
|
}
|
}
|
*p++ = wct; /* wct */
|
*p++ = wct; /* wct */
|
p += 2 * wct;
|
p += 2 * wct;
|
WSET(p, 0, bcc);
|
WSET(p, 0, bcc);
|
return p + 2;
|
return p + 2;
|
}
|
}
|
|
|
/* smb_setup_header_exclusive waits on server->lock and locks the
|
/* smb_setup_header_exclusive waits on server->lock and locks the
|
server, when it's free. You have to unlock it manually when you're
|
server, when it's free. You have to unlock it manually when you're
|
finished with server->packet! */
|
finished with server->packet! */
|
|
|
static byte *
|
static byte *
|
smb_setup_header_exclusive(struct smb_server *server,
|
smb_setup_header_exclusive(struct smb_server *server,
|
byte command, word wct, word bcc)
|
byte command, word wct, word bcc)
|
{
|
{
|
smb_lock_server(server);
|
smb_lock_server(server);
|
return smb_setup_header(server, command, wct, bcc);
|
return smb_setup_header(server, command, wct, bcc);
|
}
|
}
|
|
|
static void
|
static void
|
smb_setup_bcc(struct smb_server *server, byte * p)
|
smb_setup_bcc(struct smb_server *server, byte * p)
|
{
|
{
|
__u8 *packet = server->packet;
|
__u8 *packet = server->packet;
|
__u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
|
__u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
|
__u16 bcc = p - (pbcc + 2);
|
__u16 bcc = p - (pbcc + 2);
|
|
|
WSET(pbcc, 0, bcc);
|
WSET(pbcc, 0, bcc);
|
smb_encode_smb_length(packet,
|
smb_encode_smb_length(packet,
|
SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
|
SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
|
}
|
}
|
|
|
|
|
/*****************************************************************************/
|
/*****************************************************************************/
|
/* */
|
/* */
|
/* File operation section. */
|
/* File operation section. */
|
/* */
|
/* */
|
/*****************************************************************************/
|
/*****************************************************************************/
|
|
|
int
|
int
|
smb_proc_open(struct smb_server *server,
|
smb_proc_open(struct smb_server *server,
|
struct smb_inode_info *dir, const char *name, int len,
|
struct smb_inode_info *dir, const char *name, int len,
|
struct smb_dirent *entry)
|
struct smb_dirent *entry)
|
{
|
{
|
int error;
|
int error;
|
char *p;
|
char *p;
|
char *buf;
|
char *buf;
|
const word o_attr = aSYSTEM | aHIDDEN | aDIR;
|
const word o_attr = aSYSTEM | aHIDDEN | aDIR;
|
|
|
DPRINTK("smb_proc_open: name=%s\n", name);
|
DPRINTK("smb_proc_open: name=%s\n", name);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
if (entry->opened != 0)
|
if (entry->opened != 0)
|
{
|
{
|
/* Somebody else opened the file while we slept */
|
/* Somebody else opened the file while we slept */
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return 0;
|
return 0;
|
}
|
}
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBopen, 2, 0);
|
p = smb_setup_header(server, SMBopen, 2, 0);
|
WSET(buf, smb_vwv0, 0x42); /* read/write */
|
WSET(buf, smb_vwv0, 0x42); /* read/write */
|
WSET(buf, smb_vwv1, o_attr);
|
WSET(buf, smb_vwv1, o_attr);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, dir, name, len);
|
p = smb_encode_path(server, p, dir, name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
|
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
|
{
|
{
|
|
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
if ((error != -EACCES) && (error != -ETXTBSY)
|
if ((error != -EACCES) && (error != -ETXTBSY)
|
&& (error != -EROFS))
|
&& (error != -EROFS))
|
{
|
{
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return error;
|
return error;
|
}
|
}
|
/* N.B. Packet may change after request */
|
/* N.B. Packet may change after request */
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBopen, 2, 0);
|
p = smb_setup_header(server, SMBopen, 2, 0);
|
WSET(buf, smb_vwv0, 0x40); /* read only */
|
WSET(buf, smb_vwv0, 0x40); /* read only */
|
WSET(buf, smb_vwv1, o_attr);
|
WSET(buf, smb_vwv1, o_attr);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, dir, name, len);
|
p = smb_encode_path(server, p, dir, name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
|
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return error;
|
return error;
|
}
|
}
|
}
|
}
|
/* We should now have data in vwv[0..6]. */
|
/* We should now have data in vwv[0..6]. */
|
|
|
/* N.B. Packet may change after request */
|
/* N.B. Packet may change after request */
|
buf = server->packet;
|
buf = server->packet;
|
entry->fileid = WVAL(buf, smb_vwv0);
|
entry->fileid = WVAL(buf, smb_vwv0);
|
entry->attr = WVAL(buf, smb_vwv1);
|
entry->attr = WVAL(buf, smb_vwv1);
|
entry->f_ctime = entry->f_atime =
|
entry->f_ctime = entry->f_atime =
|
entry->f_mtime = local2utc(DVAL(buf, smb_vwv2));
|
entry->f_mtime = local2utc(DVAL(buf, smb_vwv2));
|
entry->f_size = DVAL(buf, smb_vwv4);
|
entry->f_size = DVAL(buf, smb_vwv4);
|
entry->access = WVAL(buf, smb_vwv6);
|
entry->access = WVAL(buf, smb_vwv6);
|
|
|
entry->opened = 1;
|
entry->opened = 1;
|
entry->access &= 3;
|
entry->access &= 3;
|
|
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
|
|
DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
|
DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_close(struct smb_server *server,
|
smb_proc_close(struct smb_server *server,
|
__u16 fileid, __u32 mtime)
|
__u16 fileid, __u32 mtime)
|
{
|
{
|
char *buf;
|
char *buf;
|
|
|
smb_setup_header_exclusive(server, SMBclose, 3, 0);
|
smb_setup_header_exclusive(server, SMBclose, 3, 0);
|
buf = server->packet;
|
buf = server->packet;
|
WSET(buf, smb_vwv0, fileid);
|
WSET(buf, smb_vwv0, fileid);
|
DSET(buf, smb_vwv1, utc2local(mtime));
|
DSET(buf, smb_vwv1, utc2local(mtime));
|
|
|
return smb_request_ok_unlock(server, SMBclose, 0, 0);
|
return smb_request_ok_unlock(server, SMBclose, 0, 0);
|
}
|
}
|
|
|
/* In smb_proc_read and smb_proc_write we do not retry, because the
|
/* In smb_proc_read and smb_proc_write we do not retry, because the
|
file-id would not be valid after a reconnection. */
|
file-id would not be valid after a reconnection. */
|
|
|
/* smb_proc_read: fs indicates if it should be copied with
|
/* smb_proc_read: fs indicates if it should be copied with
|
memcpy_tofs. */
|
memcpy_tofs. */
|
|
|
int
|
int
|
smb_proc_read(struct smb_server *server, struct smb_dirent *finfo,
|
smb_proc_read(struct smb_server *server, struct smb_dirent *finfo,
|
off_t offset, long count, char *data, int fs)
|
off_t offset, long count, char *data, int fs)
|
{
|
{
|
word returned_count, data_len;
|
word returned_count, data_len;
|
char *buf;
|
char *buf;
|
int error;
|
int error;
|
|
|
smb_setup_header_exclusive(server, SMBread, 5, 0);
|
smb_setup_header_exclusive(server, SMBread, 5, 0);
|
buf = server->packet;
|
buf = server->packet;
|
|
|
WSET(buf, smb_vwv0, finfo->fileid);
|
WSET(buf, smb_vwv0, finfo->fileid);
|
WSET(buf, smb_vwv1, count);
|
WSET(buf, smb_vwv1, count);
|
DSET(buf, smb_vwv2, offset);
|
DSET(buf, smb_vwv2, offset);
|
WSET(buf, smb_vwv4, 0);
|
WSET(buf, smb_vwv4, 0);
|
|
|
if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
|
if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
|
{
|
{
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return error;
|
return error;
|
}
|
}
|
returned_count = WVAL(server->packet, smb_vwv0);
|
returned_count = WVAL(server->packet, smb_vwv0);
|
|
|
smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
|
smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
|
|
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
|
|
if (returned_count != data_len)
|
if (returned_count != data_len)
|
{
|
{
|
printk("smb_proc_read: Warning, returned_count != data_len\n");
|
printk("smb_proc_read: Warning, returned_count != data_len\n");
|
printk("smb_proc_read: ret_c=%d, data_len=%d\n",
|
printk("smb_proc_read: ret_c=%d, data_len=%d\n",
|
returned_count, data_len);
|
returned_count, data_len);
|
}
|
}
|
return data_len;
|
return data_len;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
|
smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
|
off_t offset, int count, const char *data)
|
off_t offset, int count, const char *data)
|
{
|
{
|
int res = 0;
|
int res = 0;
|
char *buf;
|
char *buf;
|
byte *p;
|
byte *p;
|
|
|
p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
|
p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
|
buf = server->packet;
|
buf = server->packet;
|
WSET(buf, smb_vwv0, finfo->fileid);
|
WSET(buf, smb_vwv0, finfo->fileid);
|
WSET(buf, smb_vwv1, count);
|
WSET(buf, smb_vwv1, count);
|
DSET(buf, smb_vwv2, offset);
|
DSET(buf, smb_vwv2, offset);
|
WSET(buf, smb_vwv4, 0);
|
WSET(buf, smb_vwv4, 0);
|
|
|
*p++ = 1;
|
*p++ = 1;
|
WSET(p, 0, count);
|
WSET(p, 0, count);
|
memcpy_fromfs(p + 2, data, count);
|
memcpy_fromfs(p + 2, data, count);
|
|
|
if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
|
if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
|
{
|
{
|
res = WVAL(server->packet, smb_vwv0);
|
res = WVAL(server->packet, smb_vwv0);
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
|
|
return res;
|
return res;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_create(struct inode *dir, const char *name, int len,
|
smb_proc_create(struct inode *dir, const char *name, int len,
|
word attr, time_t ctime)
|
word attr, time_t ctime)
|
{
|
{
|
int error;
|
int error;
|
char *p;
|
char *p;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
char *buf;
|
char *buf;
|
__u16 fileid;
|
__u16 fileid;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBcreate, 3, 0);
|
p = smb_setup_header(server, SMBcreate, 3, 0);
|
WSET(buf, smb_vwv0, attr);
|
WSET(buf, smb_vwv0, attr);
|
DSET(buf, smb_vwv1, utc2local(ctime));
|
DSET(buf, smb_vwv1, utc2local(ctime));
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
|
if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return error;
|
return error;
|
}
|
}
|
fileid = WVAL(server->packet, smb_vwv0);
|
fileid = WVAL(server->packet, smb_vwv0);
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
|
|
smb_proc_close(server, fileid, CURRENT_TIME);
|
smb_proc_close(server, fileid, CURRENT_TIME);
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_mv(struct inode *odir, const char *oname, const int olen,
|
smb_proc_mv(struct inode *odir, const char *oname, const int olen,
|
struct inode *ndir, const char *nname, const int nlen)
|
struct inode *ndir, const char *nname, const int nlen)
|
{
|
{
|
char *p;
|
char *p;
|
struct smb_server *server = SMB_SERVER(odir);
|
struct smb_server *server = SMB_SERVER(odir);
|
int result;
|
int result;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
p = smb_setup_header(server, SMBmv, 1, 0);
|
p = smb_setup_header(server, SMBmv, 1, 0);
|
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
|
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen);
|
p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen);
|
p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_mkdir(struct inode *dir, const char *name, const int len)
|
smb_proc_mkdir(struct inode *dir, const char *name, const int len)
|
{
|
{
|
char *p;
|
char *p;
|
int result;
|
int result;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
p = smb_setup_header(server, SMBmkdir, 0, 0);
|
p = smb_setup_header(server, SMBmkdir, 0, 0);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_rmdir(struct inode *dir, const char *name, const int len)
|
smb_proc_rmdir(struct inode *dir, const char *name, const int len)
|
{
|
{
|
char *p;
|
char *p;
|
int result;
|
int result;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
p = smb_setup_header(server, SMBrmdir, 0, 0);
|
p = smb_setup_header(server, SMBrmdir, 0, 0);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_unlink(struct inode *dir, const char *name, const int len)
|
smb_proc_unlink(struct inode *dir, const char *name, const int len)
|
{
|
{
|
char *p;
|
char *p;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
int result;
|
int result;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
p = smb_setup_header(server, SMBunlink, 1, 0);
|
p = smb_setup_header(server, SMBunlink, 1, 0);
|
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
|
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_trunc(struct smb_server *server, word fid, dword length)
|
smb_proc_trunc(struct smb_server *server, word fid, dword length)
|
{
|
{
|
char *p;
|
char *p;
|
char *buf;
|
char *buf;
|
int result;
|
int result;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBwrite, 5, 0);
|
p = smb_setup_header(server, SMBwrite, 5, 0);
|
WSET(buf, smb_vwv0, fid);
|
WSET(buf, smb_vwv0, fid);
|
WSET(buf, smb_vwv1, 0);
|
WSET(buf, smb_vwv1, 0);
|
DSET(buf, smb_vwv2, length);
|
DSET(buf, smb_vwv2, length);
|
WSET(buf, smb_vwv4, 0);
|
WSET(buf, smb_vwv4, 0);
|
p = smb_encode_ascii(p, "", 0);
|
p = smb_encode_ascii(p, "", 0);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
static void
|
static void
|
smb_init_dirent(struct smb_server *server, struct smb_dirent *entry)
|
smb_init_dirent(struct smb_server *server, struct smb_dirent *entry)
|
{
|
{
|
memset(entry, 0, sizeof(struct smb_dirent));
|
memset(entry, 0, sizeof(struct smb_dirent));
|
|
|
entry->f_nlink = 1;
|
entry->f_nlink = 1;
|
entry->f_uid = server->m.uid;
|
entry->f_uid = server->m.uid;
|
entry->f_gid = server->m.gid;
|
entry->f_gid = server->m.gid;
|
entry->f_blksize = 512;
|
entry->f_blksize = 512;
|
}
|
}
|
|
|
static void
|
static void
|
smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry)
|
smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry)
|
{
|
{
|
if ((entry->attr & aDIR) != 0)
|
if ((entry->attr & aDIR) != 0)
|
{
|
{
|
entry->f_mode = server->m.dir_mode;
|
entry->f_mode = server->m.dir_mode;
|
entry->f_size = 512;
|
entry->f_size = 512;
|
} else
|
} else
|
{
|
{
|
entry->f_mode = server->m.file_mode;
|
entry->f_mode = server->m.file_mode;
|
}
|
}
|
|
|
if (entry->attr & aRONLY)
|
if (entry->attr & aRONLY)
|
entry->f_mode &= ~0222;
|
entry->f_mode &= ~0222;
|
|
|
if ((entry->f_blksize != 0) && (entry->f_size != 0))
|
if ((entry->f_blksize != 0) && (entry->f_size != 0))
|
{
|
{
|
entry->f_blocks =
|
entry->f_blocks =
|
(entry->f_size - 1) / entry->f_blksize + 1;
|
(entry->f_size - 1) / entry->f_blksize + 1;
|
} else
|
} else
|
{
|
{
|
entry->f_blocks = 0;
|
entry->f_blocks = 0;
|
}
|
}
|
return;
|
return;
|
}
|
}
|
|
|
void
|
void
|
smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry)
|
smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry)
|
{
|
{
|
smb_init_dirent(server, entry);
|
smb_init_dirent(server, entry);
|
entry->attr = aDIR;
|
entry->attr = aDIR;
|
entry->f_ino = 1;
|
entry->f_ino = 1;
|
smb_finish_dirent(server, entry);
|
smb_finish_dirent(server, entry);
|
}
|
}
|
|
|
|
|
static char *
|
static char *
|
smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry)
|
smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry)
|
{
|
{
|
smb_init_dirent(server, entry);
|
smb_init_dirent(server, entry);
|
|
|
p += SMB_STATUS_SIZE; /* reserved (search_status) */
|
p += SMB_STATUS_SIZE; /* reserved (search_status) */
|
entry->attr = BVAL(p, 0);
|
entry->attr = BVAL(p, 0);
|
entry->f_mtime = entry->f_atime = entry->f_ctime =
|
entry->f_mtime = entry->f_atime = entry->f_ctime =
|
date_dos2unix(WVAL(p, 1), WVAL(p, 3));
|
date_dos2unix(WVAL(p, 1), WVAL(p, 3));
|
entry->f_size = DVAL(p, 5);
|
entry->f_size = DVAL(p, 5);
|
entry->len = strlen(p + 9);
|
entry->len = strlen(p + 9);
|
if (entry->len > 12)
|
if (entry->len > 12)
|
{
|
{
|
entry->len = 12;
|
entry->len = 12;
|
}
|
}
|
memcpy(entry->name, p + 9, entry->len);
|
memcpy(entry->name, p + 9, entry->len);
|
entry->name[entry->len] = '\0';
|
entry->name[entry->len] = '\0';
|
while (entry->len > 2)
|
while (entry->len > 2)
|
{
|
{
|
/* Pathworks fills names with spaces */
|
/* Pathworks fills names with spaces */
|
entry->len -= 1;
|
entry->len -= 1;
|
if (entry->name[entry->len] == ' ')
|
if (entry->name[entry->len] == ' ')
|
{
|
{
|
entry->name[entry->len] = '\0';
|
entry->name[entry->len] = '\0';
|
}
|
}
|
}
|
}
|
switch (server->case_handling)
|
switch (server->case_handling)
|
{
|
{
|
case CASE_UPPER:
|
case CASE_UPPER:
|
str_upper(entry->name);
|
str_upper(entry->name);
|
break;
|
break;
|
case CASE_LOWER:
|
case CASE_LOWER:
|
str_lower(entry->name);
|
str_lower(entry->name);
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
DPRINTK("smb_decode_dirent: name = %s\n", entry->name);
|
DPRINTK("smb_decode_dirent: name = %s\n", entry->name);
|
smb_finish_dirent(server, entry);
|
smb_finish_dirent(server, entry);
|
return p + 22;
|
return p + 22;
|
}
|
}
|
|
|
/* This routine is used to read in directory entries from the network.
|
/* This routine is used to read in directory entries from the network.
|
Note that it is for short directory name seeks, i.e.: protocol <
|
Note that it is for short directory name seeks, i.e.: protocol <
|
PROTOCOL_LANMAN2 */
|
PROTOCOL_LANMAN2 */
|
|
|
static int
|
static int
|
smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
|
smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
|
int cache_size, struct smb_dirent *entry)
|
int cache_size, struct smb_dirent *entry)
|
{
|
{
|
char *p;
|
char *p;
|
char *buf;
|
char *buf;
|
int error;
|
int error;
|
int result;
|
int result;
|
int i;
|
int i;
|
int first, total_count;
|
int first, total_count;
|
struct smb_dirent *current_entry;
|
struct smb_dirent *current_entry;
|
word bcc;
|
word bcc;
|
word count;
|
word count;
|
char status[SMB_STATUS_SIZE];
|
char status[SMB_STATUS_SIZE];
|
int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
|
int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
|
|
|
DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos);
|
DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
first = 1;
|
first = 1;
|
total_count = 0;
|
total_count = 0;
|
current_entry = entry;
|
current_entry = entry;
|
|
|
while (1)
|
while (1)
|
{
|
{
|
if (first == 1)
|
if (first == 1)
|
{
|
{
|
p = smb_setup_header(server, SMBsearch, 2, 0);
|
p = smb_setup_header(server, SMBsearch, 2, 0);
|
WSET(buf, smb_vwv0, entries_asked);
|
WSET(buf, smb_vwv0, entries_asked);
|
WSET(buf, smb_vwv1, aDIR);
|
WSET(buf, smb_vwv1, aDIR);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3);
|
p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3);
|
*p++ = 5;
|
*p++ = 5;
|
WSET(p, 0, 0);
|
WSET(p, 0, 0);
|
p += 2;
|
p += 2;
|
} else
|
} else
|
{
|
{
|
p = smb_setup_header(server, SMBsearch, 2, 0);
|
p = smb_setup_header(server, SMBsearch, 2, 0);
|
WSET(buf, smb_vwv0, entries_asked);
|
WSET(buf, smb_vwv0, entries_asked);
|
WSET(buf, smb_vwv1, aDIR);
|
WSET(buf, smb_vwv1, aDIR);
|
p = smb_encode_ascii(p, "", 0);
|
p = smb_encode_ascii(p, "", 0);
|
*p++ = 5;
|
*p++ = 5;
|
WSET(p, 0, SMB_STATUS_SIZE);
|
WSET(p, 0, SMB_STATUS_SIZE);
|
p += 2;
|
p += 2;
|
memcpy(p, status, SMB_STATUS_SIZE);
|
memcpy(p, status, SMB_STATUS_SIZE);
|
p += SMB_STATUS_SIZE;
|
p += SMB_STATUS_SIZE;
|
}
|
}
|
|
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
|
if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
|
{
|
{
|
if ((server->rcls == ERRDOS)
|
if ((server->rcls == ERRDOS)
|
&& (server->err == ERRnofiles))
|
&& (server->err == ERRnofiles))
|
{
|
{
|
result = total_count - fpos;
|
result = total_count - fpos;
|
goto unlock_return;
|
goto unlock_return;
|
} else
|
} else
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
result = error;
|
result = error;
|
goto unlock_return;
|
goto unlock_return;
|
}
|
}
|
}
|
}
|
p = SMB_VWV(server->packet);
|
p = SMB_VWV(server->packet);
|
p = smb_decode_word(p, &count);
|
p = smb_decode_word(p, &count);
|
p = smb_decode_word(p, &bcc);
|
p = smb_decode_word(p, &bcc);
|
|
|
first = 0;
|
first = 0;
|
|
|
if (count <= 0)
|
if (count <= 0)
|
{
|
{
|
result = total_count - fpos;
|
result = total_count - fpos;
|
goto unlock_return;
|
goto unlock_return;
|
}
|
}
|
if (bcc != count * SMB_DIRINFO_SIZE + 3)
|
if (bcc != count * SMB_DIRINFO_SIZE + 3)
|
{
|
{
|
result = -EIO;
|
result = -EIO;
|
goto unlock_return;
|
goto unlock_return;
|
}
|
}
|
p += 3; /* Skipping VBLOCK header
|
p += 3; /* Skipping VBLOCK header
|
(5, length lo, length hi). */
|
(5, length lo, length hi). */
|
|
|
/* Read the last entry into the status field. */
|
/* Read the last entry into the status field. */
|
memcpy(status,
|
memcpy(status,
|
SMB_BUF(server->packet) + 3 +
|
SMB_BUF(server->packet) + 3 +
|
(count - 1) * SMB_DIRINFO_SIZE,
|
(count - 1) * SMB_DIRINFO_SIZE,
|
SMB_STATUS_SIZE);
|
SMB_STATUS_SIZE);
|
|
|
/* Now we are ready to parse smb directory entries. */
|
/* Now we are ready to parse smb directory entries. */
|
|
|
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
{
|
{
|
if (total_count < fpos)
|
if (total_count < fpos)
|
{
|
{
|
p += SMB_DIRINFO_SIZE;
|
p += SMB_DIRINFO_SIZE;
|
DDPRINTK("smb_proc_readdir: skipped entry.\n");
|
DDPRINTK("smb_proc_readdir: skipped entry.\n");
|
DDPRINTK(" total_count = %d\n"
|
DDPRINTK(" total_count = %d\n"
|
" i = %d, fpos = %d\n",
|
" i = %d, fpos = %d\n",
|
total_count, i, fpos);
|
total_count, i, fpos);
|
} else if (total_count >= fpos + cache_size)
|
} else if (total_count >= fpos + cache_size)
|
{
|
{
|
result = total_count - fpos;
|
result = total_count - fpos;
|
goto unlock_return;
|
goto unlock_return;
|
} else
|
} else
|
{
|
{
|
p = smb_decode_dirent(server, p,
|
p = smb_decode_dirent(server, p,
|
current_entry);
|
current_entry);
|
current_entry->f_pos = total_count;
|
current_entry->f_pos = total_count;
|
DDPRINTK("smb_proc_readdir: entry->f_pos = "
|
DDPRINTK("smb_proc_readdir: entry->f_pos = "
|
"%lu\n", entry->f_pos);
|
"%lu\n", entry->f_pos);
|
current_entry += 1;
|
current_entry += 1;
|
}
|
}
|
total_count += 1;
|
total_count += 1;
|
}
|
}
|
}
|
}
|
unlock_return:
|
unlock_return:
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
/* interpret a long filename structure - this is mostly guesses at the
|
/* interpret a long filename structure - this is mostly guesses at the
|
moment. The length of the structure is returned. The structure of
|
moment. The length of the structure is returned. The structure of
|
a long filename depends on the info level. 260 is used by NT and 2
|
a long filename depends on the info level. 260 is used by NT and 2
|
is used by OS/2. */
|
is used by OS/2. */
|
|
|
static char *
|
static char *
|
smb_decode_long_dirent(struct smb_server *server, char *p,
|
smb_decode_long_dirent(struct smb_server *server, char *p,
|
struct smb_dirent *entry, int level)
|
struct smb_dirent *entry, int level)
|
{
|
{
|
char *result;
|
char *result;
|
|
|
smb_init_dirent(server, entry);
|
smb_init_dirent(server, entry);
|
|
|
switch (level)
|
switch (level)
|
{
|
{
|
/* We might add more levels later... */
|
/* We might add more levels later... */
|
case 1:
|
case 1:
|
entry->len = BVAL(p, 26);
|
entry->len = BVAL(p, 26);
|
strncpy(entry->name, p + 27, entry->len);
|
strncpy(entry->name, p + 27, entry->len);
|
entry->name[entry->len] = '\0';
|
entry->name[entry->len] = '\0';
|
entry->f_size = DVAL(p, 16);
|
entry->f_size = DVAL(p, 16);
|
entry->attr = BVAL(p, 24);
|
entry->attr = BVAL(p, 24);
|
|
|
entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
|
entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
|
entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
|
entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
|
entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
|
entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
|
result = p + 28 + BVAL(p, 26);
|
result = p + 28 + BVAL(p, 26);
|
break;
|
break;
|
|
|
default:
|
default:
|
DPRINTK("Unknown long filename format %d\n", level);
|
DPRINTK("Unknown long filename format %d\n", level);
|
result = p + WVAL(p, 0);
|
result = p + WVAL(p, 0);
|
}
|
}
|
|
|
switch (server->case_handling)
|
switch (server->case_handling)
|
{
|
{
|
case CASE_UPPER:
|
case CASE_UPPER:
|
str_upper(entry->name);
|
str_upper(entry->name);
|
break;
|
break;
|
case CASE_LOWER:
|
case CASE_LOWER:
|
str_lower(entry->name);
|
str_lower(entry->name);
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
smb_finish_dirent(server, entry);
|
smb_finish_dirent(server, entry);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
|
smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
|
int cache_size, struct smb_dirent *cache)
|
int cache_size, struct smb_dirent *cache)
|
{
|
{
|
/* NT uses 260, OS/2 uses 2. Both accept 1. */
|
/* NT uses 260, OS/2 uses 2. Both accept 1. */
|
const int info_level = 1;
|
const int info_level = 1;
|
const int max_matches = 512;
|
const int max_matches = 512;
|
|
|
char *p;
|
char *p;
|
char *lastname;
|
char *lastname;
|
int lastname_len;
|
int lastname_len;
|
int i;
|
int i;
|
int first, entries, entries_seen;
|
int first, entries, entries_seen;
|
|
|
unsigned char *resp_data = NULL;
|
unsigned char *resp_data = NULL;
|
unsigned char *resp_param = NULL;
|
unsigned char *resp_param = NULL;
|
int resp_data_len = 0;
|
int resp_data_len = 0;
|
int resp_param_len = 0;
|
int resp_param_len = 0;
|
|
|
__u16 command;
|
__u16 command;
|
|
|
int result;
|
int result;
|
|
|
int ff_resume_key = 0;
|
int ff_resume_key = 0;
|
int ff_searchcount = 0;
|
int ff_searchcount = 0;
|
int ff_eos = 0;
|
int ff_eos = 0;
|
int ff_lastname = 0;
|
int ff_lastname = 0;
|
int ff_dir_handle = 0;
|
int ff_dir_handle = 0;
|
int loop_count = 0;
|
int loop_count = 0;
|
|
|
char param[SMB_MAXPATHLEN + 2 + 12];
|
char param[SMB_MAXPATHLEN + 2 + 12];
|
int mask_len;
|
int mask_len;
|
unsigned char *mask = &(param[12]);
|
unsigned char *mask = &(param[12]);
|
|
|
mask_len = smb_encode_path(server, mask,
|
mask_len = smb_encode_path(server, mask,
|
SMB_INOP(dir), "*", 1) - mask;
|
SMB_INOP(dir), "*", 1) - mask;
|
|
|
mask[mask_len] = 0;
|
mask[mask_len] = 0;
|
mask[mask_len + 1] = 0;
|
mask[mask_len + 1] = 0;
|
|
|
DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
|
DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
|
cache_size, fpos, mask);
|
cache_size, fpos, mask);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
|
|
first = 1;
|
first = 1;
|
entries = 0;
|
entries = 0;
|
entries_seen = 2;
|
entries_seen = 2;
|
|
|
while (ff_eos == 0)
|
while (ff_eos == 0)
|
{
|
{
|
loop_count += 1;
|
loop_count += 1;
|
if (loop_count > 200)
|
if (loop_count > 200)
|
{
|
{
|
printk("smb_proc_readdir_long: "
|
printk("smb_proc_readdir_long: "
|
"Looping in FIND_NEXT??\n");
|
"Looping in FIND_NEXT??\n");
|
break;
|
break;
|
}
|
}
|
if (first != 0)
|
if (first != 0)
|
{
|
{
|
command = TRANSACT2_FINDFIRST;
|
command = TRANSACT2_FINDFIRST;
|
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
|
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
|
WSET(param, 2, max_matches); /* max count */
|
WSET(param, 2, max_matches); /* max count */
|
WSET(param, 4, 8 + 4 + 2); /* resume required +
|
WSET(param, 4, 8 + 4 + 2); /* resume required +
|
close on end +
|
close on end +
|
continue */
|
continue */
|
WSET(param, 6, info_level);
|
WSET(param, 6, info_level);
|
DSET(param, 8, 0);
|
DSET(param, 8, 0);
|
} else
|
} else
|
{
|
{
|
command = TRANSACT2_FINDNEXT;
|
command = TRANSACT2_FINDNEXT;
|
DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
|
DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
|
ff_dir_handle, ff_resume_key, ff_lastname, mask);
|
ff_dir_handle, ff_resume_key, ff_lastname, mask);
|
WSET(param, 0, ff_dir_handle);
|
WSET(param, 0, ff_dir_handle);
|
WSET(param, 2, max_matches); /* max count */
|
WSET(param, 2, max_matches); /* max count */
|
WSET(param, 4, info_level);
|
WSET(param, 4, info_level);
|
DSET(param, 6, ff_resume_key); /* ff_resume_key */
|
DSET(param, 6, ff_resume_key); /* ff_resume_key */
|
WSET(param, 10, 8 + 4 + 2); /* resume required +
|
WSET(param, 10, 8 + 4 + 2); /* resume required +
|
close on end +
|
close on end +
|
continue */
|
continue */
|
}
|
}
|
#ifdef CONFIG_SMB_WIN95
|
#ifdef CONFIG_SMB_WIN95
|
/* Windows 95 is not able to deliver answers
|
/* Windows 95 is not able to deliver answers
|
to FIND_NEXT fast enough, so sleep 0.2 seconds */
|
to FIND_NEXT fast enough, so sleep 0.2 seconds */
|
current->timeout = jiffies + HZ / 5;
|
current->timeout = jiffies + HZ / 5;
|
current->state = TASK_INTERRUPTIBLE;
|
current->state = TASK_INTERRUPTIBLE;
|
schedule();
|
schedule();
|
current->timeout = 0;
|
current->timeout = 0;
|
#endif
|
#endif
|
|
|
result = smb_trans2_request(server, command,
|
result = smb_trans2_request(server, command,
|
0, NULL, 12 + mask_len + 2, param,
|
0, NULL, 12 + mask_len + 2, param,
|
&resp_data_len, &resp_data,
|
&resp_data_len, &resp_data,
|
&resp_param_len, &resp_param);
|
&resp_param_len, &resp_param);
|
|
|
if (result < 0)
|
if (result < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
DPRINTK("smb_proc_readdir_long: "
|
DPRINTK("smb_proc_readdir_long: "
|
"got error from trans2_request\n");
|
"got error from trans2_request\n");
|
break;
|
break;
|
}
|
}
|
if (server->rcls != 0)
|
if (server->rcls != 0)
|
{
|
{
|
result = -EIO;
|
result = -EIO;
|
break;
|
break;
|
}
|
}
|
/* parse out some important return info */
|
/* parse out some important return info */
|
if (first != 0)
|
if (first != 0)
|
{
|
{
|
ff_dir_handle = WVAL(resp_param, 0);
|
ff_dir_handle = WVAL(resp_param, 0);
|
ff_searchcount = WVAL(resp_param, 2);
|
ff_searchcount = WVAL(resp_param, 2);
|
ff_eos = WVAL(resp_param, 4);
|
ff_eos = WVAL(resp_param, 4);
|
ff_lastname = WVAL(resp_param, 8);
|
ff_lastname = WVAL(resp_param, 8);
|
} else
|
} else
|
{
|
{
|
ff_searchcount = WVAL(resp_param, 0);
|
ff_searchcount = WVAL(resp_param, 0);
|
ff_eos = WVAL(resp_param, 2);
|
ff_eos = WVAL(resp_param, 2);
|
ff_lastname = WVAL(resp_param, 6);
|
ff_lastname = WVAL(resp_param, 6);
|
}
|
}
|
|
|
if (ff_searchcount == 0)
|
if (ff_searchcount == 0)
|
{
|
{
|
break;
|
break;
|
}
|
}
|
/* point to the data bytes */
|
/* point to the data bytes */
|
p = resp_data;
|
p = resp_data;
|
|
|
/* we might need the lastname for continuations */
|
/* we might need the lastname for continuations */
|
lastname = "";
|
lastname = "";
|
lastname_len = 0;
|
lastname_len = 0;
|
if (ff_lastname > 0)
|
if (ff_lastname > 0)
|
{
|
{
|
switch (info_level)
|
switch (info_level)
|
{
|
{
|
case 260:
|
case 260:
|
lastname = p + ff_lastname;
|
lastname = p + ff_lastname;
|
lastname_len = resp_data_len - ff_lastname;
|
lastname_len = resp_data_len - ff_lastname;
|
ff_resume_key = 0;
|
ff_resume_key = 0;
|
break;
|
break;
|
case 1:
|
case 1:
|
lastname = p + ff_lastname + 1;
|
lastname = p + ff_lastname + 1;
|
lastname_len = BVAL(p, ff_lastname);
|
lastname_len = BVAL(p, ff_lastname);
|
ff_resume_key = 0;
|
ff_resume_key = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
lastname_len = min(lastname_len, 256);
|
lastname_len = min(lastname_len, 256);
|
strncpy(mask, lastname, lastname_len);
|
strncpy(mask, lastname, lastname_len);
|
mask[lastname_len] = '\0';
|
mask[lastname_len] = '\0';
|
|
|
/* Now we are ready to parse smb directory entries. */
|
/* Now we are ready to parse smb directory entries. */
|
|
|
for (i = 0; i < ff_searchcount; i++)
|
for (i = 0; i < ff_searchcount; i++)
|
{
|
{
|
struct smb_dirent *entry = &(cache[entries]);
|
struct smb_dirent *entry = &(cache[entries]);
|
|
|
p = smb_decode_long_dirent(server, p,
|
p = smb_decode_long_dirent(server, p,
|
entry, info_level);
|
entry, info_level);
|
|
|
DDPRINTK("smb_readdir_long: got %s\n", entry->name);
|
DDPRINTK("smb_readdir_long: got %s\n", entry->name);
|
|
|
if ((entry->name[0] == '.')
|
if ((entry->name[0] == '.')
|
&& ((entry->name[1] == '\0')
|
&& ((entry->name[1] == '\0')
|
|| ((entry->name[1] == '.')
|
|| ((entry->name[1] == '.')
|
&& (entry->name[2] == '\0'))))
|
&& (entry->name[2] == '\0'))))
|
{
|
{
|
/* ignore . and .. from the server */
|
/* ignore . and .. from the server */
|
continue;
|
continue;
|
}
|
}
|
if (entries_seen >= fpos)
|
if (entries_seen >= fpos)
|
{
|
{
|
entry->f_pos = entries_seen;
|
entry->f_pos = entries_seen;
|
entries += 1;
|
entries += 1;
|
}
|
}
|
if (entries >= cache_size)
|
if (entries >= cache_size)
|
{
|
{
|
goto finished;
|
goto finished;
|
}
|
}
|
entries_seen += 1;
|
entries_seen += 1;
|
}
|
}
|
|
|
DPRINTK("received %d entries (eos=%d resume=%d)\n",
|
DPRINTK("received %d entries (eos=%d resume=%d)\n",
|
ff_searchcount, ff_eos, ff_resume_key);
|
ff_searchcount, ff_eos, ff_resume_key);
|
|
|
first = 0;
|
first = 0;
|
}
|
}
|
|
|
finished:
|
finished:
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return entries;
|
return entries;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
|
smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
|
int cache_size, struct smb_dirent *entry)
|
int cache_size, struct smb_dirent *entry)
|
{
|
{
|
if (server->protocol >= PROTOCOL_LANMAN2)
|
if (server->protocol >= PROTOCOL_LANMAN2)
|
return smb_proc_readdir_long(server, dir, fpos, cache_size,
|
return smb_proc_readdir_long(server, dir, fpos, cache_size,
|
entry);
|
entry);
|
else
|
else
|
return smb_proc_readdir_short(server, dir, fpos, cache_size,
|
return smb_proc_readdir_short(server, dir, fpos, cache_size,
|
entry);
|
entry);
|
}
|
}
|
|
|
/*
|
/*
|
* This version uses the core protocol to get the attribute info.
|
* This version uses the core protocol to get the attribute info.
|
* It works OK with Win 3.11, 95 and NT 3.51, but NOT with NT 4 (bad mtime).
|
* It works OK with Win 3.11, 95 and NT 3.51, but NOT with NT 4 (bad mtime).
|
*/
|
*/
|
static int
|
static int
|
smb_proc_getattr_core(struct inode *dir, const char *name, int len,
|
smb_proc_getattr_core(struct inode *dir, const char *name, int len,
|
struct smb_dirent *entry)
|
struct smb_dirent *entry)
|
{
|
{
|
int result;
|
int result;
|
char *p;
|
char *p;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
char *buf;
|
char *buf;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
DDPRINTK("smb_proc_getattr_core: %s\n", name);
|
DDPRINTK("smb_proc_getattr_core: %s\n", name);
|
|
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBgetatr, 0, 0);
|
p = smb_setup_header(server, SMBgetatr, 0, 0);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
|
|
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
/* N.B. Packet may change after request */
|
/* N.B. Packet may change after request */
|
buf = server->packet;
|
buf = server->packet;
|
entry->attr = WVAL(buf, smb_vwv0);
|
entry->attr = WVAL(buf, smb_vwv0);
|
entry->f_ctime = entry->f_atime =
|
entry->f_ctime = entry->f_atime =
|
entry->f_mtime = local2utc(DVAL(buf, smb_vwv1));
|
entry->f_mtime = local2utc(DVAL(buf, smb_vwv1));
|
|
|
DDPRINTK("smb_proc_getattr_core: mtime=%ld\n", entry->f_mtime);
|
DDPRINTK("smb_proc_getattr_core: mtime=%ld\n", entry->f_mtime);
|
|
|
entry->f_size = DVAL(buf, smb_vwv3);
|
entry->f_size = DVAL(buf, smb_vwv3);
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* This version uses the trans2 findfirst to get the attribute info.
|
* This version uses the trans2 findfirst to get the attribute info.
|
* It works fine with NT 3.51 and NT 4 (any SP), but not with Win95 (ERRerror).
|
* It works fine with NT 3.51 and NT 4 (any SP), but not with Win95 (ERRerror).
|
*/
|
*/
|
static int
|
static int
|
smb_proc_getattr_ff(struct inode *dir, const char *name, int len,
|
smb_proc_getattr_ff(struct inode *dir, const char *name, int len,
|
struct smb_dirent *entry)
|
struct smb_dirent *entry)
|
{
|
{
|
unsigned char *resp_data = NULL;
|
unsigned char *resp_data = NULL;
|
unsigned char *resp_param = NULL;
|
unsigned char *resp_param = NULL;
|
int resp_data_len = 0;
|
int resp_data_len = 0;
|
int resp_param_len = 0;
|
int resp_param_len = 0;
|
|
|
char param[SMB_MAXPATHLEN + 1 + 12];
|
char param[SMB_MAXPATHLEN + 1 + 12];
|
int mask_len;
|
int mask_len;
|
unsigned char *mask = &(param[12]);
|
unsigned char *mask = &(param[12]);
|
|
|
int result;
|
int result;
|
char *p;
|
char *p;
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
|
|
mask_len = smb_encode_path(server, mask,
|
mask_len = smb_encode_path(server, mask,
|
SMB_INOP(dir), name, len) - mask;
|
SMB_INOP(dir), name, len) - mask;
|
|
|
mask[mask_len] = 0;
|
mask[mask_len] = 0;
|
|
|
DDPRINTK("smb_proc_getattr_ff: mask=%s\n", mask);
|
DDPRINTK("smb_proc_getattr_ff: mask=%s\n", mask);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
|
|
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
|
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
|
WSET(param, 2, 1); /* max count */
|
WSET(param, 2, 1); /* max count */
|
WSET(param, 4, 2 + 1); /* close on end + close after this call */
|
WSET(param, 4, 2 + 1); /* close on end + close after this call */
|
WSET(param, 6, 1); /* info level */
|
WSET(param, 6, 1); /* info level */
|
DSET(param, 8, 0);
|
DSET(param, 8, 0);
|
|
|
result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
|
result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
|
0, NULL, 12 + mask_len + 1, param,
|
0, NULL, 12 + mask_len + 1, param,
|
&resp_data_len, &resp_data,
|
&resp_data_len, &resp_data,
|
&resp_param_len, &resp_param);
|
&resp_param_len, &resp_param);
|
|
|
if (result < 0)
|
if (result < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
DPRINTK("smb_proc_getattr_ff: error=%d, retrying\n",
|
DPRINTK("smb_proc_getattr_ff: error=%d, retrying\n",
|
result);
|
result);
|
goto retry;
|
goto retry;
|
}
|
}
|
goto out;
|
goto out;
|
}
|
}
|
if (server->rcls != 0)
|
if (server->rcls != 0)
|
{
|
{
|
result = -smb_errno(server->rcls, server->err);
|
result = -smb_errno(server->rcls, server->err);
|
if (result != -ENOENT)
|
if (result != -ENOENT)
|
DPRINTK("smb_proc_getattr_ff: rcls=%d, err=%d\n",
|
DPRINTK("smb_proc_getattr_ff: rcls=%d, err=%d\n",
|
server->rcls, server->err);
|
server->rcls, server->err);
|
goto out;
|
goto out;
|
}
|
}
|
/* Make sure we got enough data ... */
|
/* Make sure we got enough data ... */
|
result = -EINVAL; /* WVAL(resp_param, 2) is ff_searchcount */
|
result = -EINVAL; /* WVAL(resp_param, 2) is ff_searchcount */
|
if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
|
if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
|
{
|
{
|
DPRINTK("smb_proc_getattr_ff: bad result, len=%d, count=%d\n",
|
DPRINTK("smb_proc_getattr_ff: bad result, len=%d, count=%d\n",
|
resp_data_len, WVAL(resp_param, 2));
|
resp_data_len, WVAL(resp_param, 2));
|
goto out;
|
goto out;
|
}
|
}
|
/* Decode the response (info level 1, as in smb_decode_long_dirent) */
|
/* Decode the response (info level 1, as in smb_decode_long_dirent) */
|
p = resp_data;
|
p = resp_data;
|
entry->f_ctime = date_dos2unix(WVAL(p, 2), WVAL(p, 0));
|
entry->f_ctime = date_dos2unix(WVAL(p, 2), WVAL(p, 0));
|
entry->f_atime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
|
entry->f_atime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
|
entry->f_mtime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
|
entry->f_mtime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
|
entry->f_size = DVAL(p, 12);
|
entry->f_size = DVAL(p, 12);
|
entry->attr = WVAL(p, 20);
|
entry->attr = WVAL(p, 20);
|
|
|
DDPRINTK("smb_proc_getattr_ff: attr=%x\n", entry->attr);
|
DDPRINTK("smb_proc_getattr_ff: attr=%x\n", entry->attr);
|
|
|
result = 0;
|
result = 0;
|
|
|
out:
|
out:
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_getattr(struct inode *dir, const char *name, int len,
|
smb_proc_getattr(struct inode *dir, const char *name, int len,
|
struct smb_dirent *entry)
|
struct smb_dirent *entry)
|
{
|
{
|
struct smb_server *server = SMB_SERVER(dir);
|
struct smb_server *server = SMB_SERVER(dir);
|
int result;
|
int result;
|
|
|
smb_init_dirent(server, entry);
|
smb_init_dirent(server, entry);
|
|
|
/* Use trans2 for NT, use core protocol for others (Win95/3.11/...).
|
/* Use trans2 for NT, use core protocol for others (Win95/3.11/...).
|
* We distinguish NT from Win95 by looking at the capabilities,
|
* We distinguish NT from Win95 by looking at the capabilities,
|
* in the same way as in Samba 1.9.18p2's reply.c.
|
* in the same way as in Samba 1.9.18p2's reply.c.
|
*/
|
*/
|
if ((server->protocol >= PROTOCOL_LANMAN2)
|
if ((server->protocol >= PROTOCOL_LANMAN2)
|
&& (server->blkmode & (CAP_NT_SMBS | CAP_STATUS32)))
|
&& (server->blkmode & (CAP_NT_SMBS | CAP_STATUS32)))
|
result = smb_proc_getattr_ff(dir, name, len, entry);
|
result = smb_proc_getattr_ff(dir, name, len, entry);
|
else
|
else
|
result = smb_proc_getattr_core(dir, name, len, entry);
|
result = smb_proc_getattr_core(dir, name, len, entry);
|
|
|
smb_finish_dirent(server, entry);
|
smb_finish_dirent(server, entry);
|
|
|
entry->len = len;
|
entry->len = len;
|
memcpy(entry->name, name, len);
|
memcpy(entry->name, name, len);
|
/* entry->name is null terminated from smb_init_dirent */
|
/* entry->name is null terminated from smb_init_dirent */
|
|
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_setattr(struct smb_server *server,
|
smb_proc_setattr(struct smb_server *server,
|
struct inode *i, struct smb_dirent *new_finfo)
|
struct inode *i, struct smb_dirent *new_finfo)
|
{
|
{
|
char *p;
|
char *p;
|
char *buf;
|
char *buf;
|
int result;
|
int result;
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
buf = server->packet;
|
buf = server->packet;
|
p = smb_setup_header(server, SMBsetatr, 8, 0);
|
p = smb_setup_header(server, SMBsetatr, 8, 0);
|
WSET(buf, smb_vwv0, new_finfo->attr);
|
WSET(buf, smb_vwv0, new_finfo->attr);
|
DSET(buf, smb_vwv1, 0);
|
DSET(buf, smb_vwv1, 0);
|
DSET(buf, smb_vwv3, 0);
|
DSET(buf, smb_vwv3, 0);
|
DSET(buf, smb_vwv5, 0);
|
DSET(buf, smb_vwv5, 0);
|
WSET(buf, smb_vwv7, 0);
|
WSET(buf, smb_vwv7, 0);
|
*p++ = 4;
|
*p++ = 4;
|
p = smb_encode_path(server, p,
|
p = smb_encode_path(server, p,
|
SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
|
SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
|
SMB_INOP(i)->finfo.len);
|
SMB_INOP(i)->finfo.len);
|
|
|
smb_setup_bcc(server, p);
|
smb_setup_bcc(server, p);
|
if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
|
smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
|
{
|
{
|
int error;
|
int error;
|
char *p;
|
char *p;
|
struct smb_server *server = &(SMB_SBP(super)->s_server);
|
struct smb_server *server = &(SMB_SBP(super)->s_server);
|
|
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
retry:
|
retry:
|
smb_setup_header(server, SMBdskattr, 0, 0);
|
smb_setup_header(server, SMBdskattr, 0, 0);
|
|
|
if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
|
if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
|
{
|
{
|
if (smb_retry(server))
|
if (smb_retry(server))
|
{
|
{
|
goto retry;
|
goto retry;
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return error;
|
return error;
|
}
|
}
|
p = SMB_VWV(server->packet);
|
p = SMB_VWV(server->packet);
|
p = smb_decode_word(p, &attr->total);
|
p = smb_decode_word(p, &attr->total);
|
p = smb_decode_word(p, &attr->allocblocks);
|
p = smb_decode_word(p, &attr->allocblocks);
|
p = smb_decode_word(p, &attr->blocksize);
|
p = smb_decode_word(p, &attr->blocksize);
|
p = smb_decode_word(p, &attr->free);
|
p = smb_decode_word(p, &attr->free);
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*****************************************************************************/
|
/*****************************************************************************/
|
/* */
|
/* */
|
/* Mount/umount operations. */
|
/* Mount/umount operations. */
|
/* */
|
/* */
|
/*****************************************************************************/
|
/*****************************************************************************/
|
|
|
struct smb_prots
|
struct smb_prots
|
{
|
{
|
enum smb_protocol prot;
|
enum smb_protocol prot;
|
const char *name;
|
const char *name;
|
};
|
};
|
|
|
/* smb_proc_reconnect: We expect the server to be locked, so that you
|
/* smb_proc_reconnect: We expect the server to be locked, so that you
|
can call the routine from within smb_retry. The socket must be
|
can call the routine from within smb_retry. The socket must be
|
created, like after a user-level socket()-call. It may not be
|
created, like after a user-level socket()-call. It may not be
|
connected. */
|
connected. */
|
|
|
int
|
int
|
smb_proc_reconnect(struct smb_server *server)
|
smb_proc_reconnect(struct smb_server *server)
|
{
|
{
|
struct smb_prots prots[] =
|
struct smb_prots prots[] =
|
{
|
{
|
{PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
|
{PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
|
{PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"},
|
{PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"},
|
#ifdef LANMAN1
|
#ifdef LANMAN1
|
{PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"},
|
{PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"},
|
{PROTOCOL_LANMAN1, "LANMAN1.0"},
|
{PROTOCOL_LANMAN1, "LANMAN1.0"},
|
#endif
|
#endif
|
#ifdef LANMAN2
|
#ifdef LANMAN2
|
{PROTOCOL_LANMAN2, "LM1.2X002"},
|
{PROTOCOL_LANMAN2, "LM1.2X002"},
|
#endif
|
#endif
|
#ifdef NT1
|
#ifdef NT1
|
{PROTOCOL_NT1, "NT LM 0.12"},
|
{PROTOCOL_NT1, "NT LM 0.12"},
|
{PROTOCOL_NT1, "NT LANMAN 1.0"},
|
{PROTOCOL_NT1, "NT LANMAN 1.0"},
|
#endif
|
#endif
|
{-1, NULL}};
|
{-1, NULL}};
|
char dev[] = "A:";
|
char dev[] = "A:";
|
int i, plength;
|
int i, plength;
|
int max_xmit = 1024; /* Space needed for first request. */
|
int max_xmit = 1024; /* Space needed for first request. */
|
int given_max_xmit = server->m.max_xmit;
|
int given_max_xmit = server->m.max_xmit;
|
int result;
|
int result;
|
byte *p;
|
byte *p;
|
|
|
if ((result = smb_connect(server)) < 0)
|
if ((result = smb_connect(server)) < 0)
|
{
|
{
|
DPRINTK("smb_proc_reconnect: could not smb_connect\n");
|
DPRINTK("smb_proc_reconnect: could not smb_connect\n");
|
goto fail;
|
goto fail;
|
}
|
}
|
/* Here we assume that the connection is valid */
|
/* Here we assume that the connection is valid */
|
server->state = CONN_VALID;
|
server->state = CONN_VALID;
|
|
|
if (server->packet != NULL)
|
if (server->packet != NULL)
|
{
|
{
|
smb_vfree(server->packet);
|
smb_vfree(server->packet);
|
server->packet = NULL;
|
server->packet = NULL;
|
server->packet_size = 0;
|
server->packet_size = 0;
|
}
|
}
|
server->packet = smb_vmalloc(max_xmit);
|
server->packet = smb_vmalloc(max_xmit);
|
|
|
if (server->packet == NULL)
|
if (server->packet == NULL)
|
{
|
{
|
printk("smb_proc_connect: No memory! Bailing out.\n");
|
printk("smb_proc_connect: No memory! Bailing out.\n");
|
result = -ENOMEM;
|
result = -ENOMEM;
|
goto fail;
|
goto fail;
|
}
|
}
|
server->packet_size = server->max_xmit = max_xmit;
|
server->packet_size = server->max_xmit = max_xmit;
|
|
|
/*
|
/*
|
* Start with an RFC1002 session request packet.
|
* Start with an RFC1002 session request packet.
|
*/
|
*/
|
p = server->packet + 4;
|
p = server->packet + 4;
|
|
|
p = smb_name_mangle(p, server->m.server_name);
|
p = smb_name_mangle(p, server->m.server_name);
|
p = smb_name_mangle(p, server->m.client_name);
|
p = smb_name_mangle(p, server->m.client_name);
|
|
|
smb_encode_smb_length(server->packet,
|
smb_encode_smb_length(server->packet,
|
(void *) p - (void *) (server->packet));
|
(void *) p - (void *) (server->packet));
|
|
|
server->packet[0] = 0x81; /* SESSION REQUEST */
|
server->packet[0] = 0x81; /* SESSION REQUEST */
|
|
|
if (smb_catch_keepalive(server) < 0)
|
if (smb_catch_keepalive(server) < 0)
|
{
|
{
|
printk("smb_proc_connect: could not catch_keepalives\n");
|
printk("smb_proc_connect: could not catch_keepalives\n");
|
}
|
}
|
if ((result = smb_request(server)) < 0)
|
if ((result = smb_request(server)) < 0)
|
{
|
{
|
DPRINTK("smb_proc_connect: Failed to send SESSION REQUEST.\n");
|
DPRINTK("smb_proc_connect: Failed to send SESSION REQUEST.\n");
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
goto fail;
|
goto fail;
|
}
|
}
|
if (server->packet[0] != 0x82)
|
if (server->packet[0] != 0x82)
|
{
|
{
|
printk("smb_proc_connect: Did not receive positive response "
|
printk("smb_proc_connect: Did not receive positive response "
|
"(err = %x)\n",
|
"(err = %x)\n",
|
server->packet[0]);
|
server->packet[0]);
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
result = -EIO;
|
result = -EIO;
|
goto fail;
|
goto fail;
|
}
|
}
|
DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
|
DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
|
|
|
/* Now we are ready to send a SMB Negotiate Protocol packet. */
|
/* Now we are ready to send a SMB Negotiate Protocol packet. */
|
memset(server->packet, 0, SMB_HEADER_LEN);
|
memset(server->packet, 0, SMB_HEADER_LEN);
|
|
|
plength = 0;
|
plength = 0;
|
for (i = 0; prots[i].name != NULL; i++)
|
for (i = 0; prots[i].name != NULL; i++)
|
{
|
{
|
plength += strlen(prots[i].name) + 2;
|
plength += strlen(prots[i].name) + 2;
|
}
|
}
|
|
|
smb_setup_header(server, SMBnegprot, 0, plength);
|
smb_setup_header(server, SMBnegprot, 0, plength);
|
|
|
p = SMB_BUF(server->packet);
|
p = SMB_BUF(server->packet);
|
|
|
for (i = 0; prots[i].name != NULL; i++)
|
for (i = 0; prots[i].name != NULL; i++)
|
{
|
{
|
*p++ = 2;
|
*p++ = 2;
|
strcpy(p, prots[i].name);
|
strcpy(p, prots[i].name);
|
p += strlen(prots[i].name) + 1;
|
p += strlen(prots[i].name) + 1;
|
}
|
}
|
|
|
if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0)
|
if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0)
|
{
|
{
|
DPRINTK("smb_proc_connect: Failure requesting SMBnegprot\n");
|
DPRINTK("smb_proc_connect: Failure requesting SMBnegprot\n");
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
goto fail;
|
goto fail;
|
} else
|
} else
|
{
|
{
|
DDPRINTK("smb_proc_connect: Request SMBnegprot..");
|
DDPRINTK("smb_proc_connect: Request SMBnegprot..");
|
}
|
}
|
|
|
DDPRINTK("Verified!\n");
|
DDPRINTK("Verified!\n");
|
|
|
p = SMB_VWV(server->packet);
|
p = SMB_VWV(server->packet);
|
p = smb_decode_word(p, (word *) & i);
|
p = smb_decode_word(p, (word *) & i);
|
#ifdef CONFIG_COLDFIRE
|
#ifdef CONFIG_COLDFIRE
|
/*
|
/*
|
* Not quite sure why this is needed???
|
* Not quite sure why this is needed???
|
* Maybe the byte swapping code is broken, but the result
|
* Maybe the byte swapping code is broken, but the result
|
* byte is certainly in the wrong position within the word.
|
* byte is certainly in the wrong position within the word.
|
*/
|
*/
|
i = i >> 16;
|
i = i >> 16;
|
#endif
|
#endif
|
server->protocol = prots[i].prot;
|
server->protocol = prots[i].prot;
|
|
|
DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
|
DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
|
prots[i].name);
|
prots[i].name);
|
|
|
if (server->protocol >= PROTOCOL_LANMAN1)
|
if (server->protocol >= PROTOCOL_LANMAN1)
|
{
|
{
|
|
|
word passlen = strlen(server->m.password);
|
word passlen = strlen(server->m.password);
|
word userlen = strlen(server->m.username);
|
word userlen = strlen(server->m.username);
|
|
|
#ifdef DEBUG_SMB_PASSWORD
|
#ifdef DEBUG_SMB_PASSWORD
|
DPRINTK("smb_proc_connect: password = %s\n",
|
DPRINTK("smb_proc_connect: password = %s\n",
|
server->m.password);
|
server->m.password);
|
#endif
|
#endif
|
DPRINTK("smb_proc_connect: usernam = %s\n",
|
DPRINTK("smb_proc_connect: usernam = %s\n",
|
server->m.username);
|
server->m.username);
|
|
|
if (server->protocol >= PROTOCOL_NT1)
|
if (server->protocol >= PROTOCOL_NT1)
|
{
|
{
|
server->max_xmit = DVAL(server->packet, smb_vwv3 + 1);
|
server->max_xmit = DVAL(server->packet, smb_vwv3 + 1);
|
server->maxmux = WVAL(server->packet, smb_vwv1 + 1);
|
server->maxmux = WVAL(server->packet, smb_vwv1 + 1);
|
server->maxvcs = WVAL(server->packet, smb_vwv2 + 1);
|
server->maxvcs = WVAL(server->packet, smb_vwv2 + 1);
|
server->blkmode = DVAL(server->packet, smb_vwv9 + 1);
|
server->blkmode = DVAL(server->packet, smb_vwv9 + 1);
|
server->sesskey = DVAL(server->packet, smb_vwv7 + 1);
|
server->sesskey = DVAL(server->packet, smb_vwv7 + 1);
|
} else
|
} else
|
{
|
{
|
server->max_xmit = WVAL(server->packet, smb_vwv2);
|
server->max_xmit = WVAL(server->packet, smb_vwv2);
|
server->maxmux = WVAL(server->packet, smb_vwv3);
|
server->maxmux = WVAL(server->packet, smb_vwv3);
|
server->maxvcs = WVAL(server->packet, smb_vwv4);
|
server->maxvcs = WVAL(server->packet, smb_vwv4);
|
server->blkmode = WVAL(server->packet, smb_vwv5);
|
server->blkmode = WVAL(server->packet, smb_vwv5);
|
server->sesskey = DVAL(server->packet, smb_vwv6);
|
server->sesskey = DVAL(server->packet, smb_vwv6);
|
}
|
}
|
DPRINTK("smb_proc_connect: blkmode (capabilities) = %x\n",
|
DPRINTK("smb_proc_connect: blkmode (capabilities) = %x\n",
|
server->blkmode);
|
server->blkmode);
|
|
|
if (server->max_xmit < given_max_xmit)
|
if (server->max_xmit < given_max_xmit)
|
{
|
{
|
/* We do not distinguish between the client
|
/* We do not distinguish between the client
|
requests and the server response. */
|
requests and the server response. */
|
given_max_xmit = server->max_xmit;
|
given_max_xmit = server->max_xmit;
|
}
|
}
|
if (server->protocol >= PROTOCOL_NT1)
|
if (server->protocol >= PROTOCOL_NT1)
|
{
|
{
|
char *workgroup = server->m.domain;
|
char *workgroup = server->m.domain;
|
char *OS_id = "Unix";
|
char *OS_id = "Unix";
|
char *client_id = "ksmbfs";
|
char *client_id = "ksmbfs";
|
|
|
smb_setup_header(server, SMBsesssetupX, 13,
|
smb_setup_header(server, SMBsesssetupX, 13,
|
5 + userlen + passlen +
|
5 + userlen + passlen +
|
strlen(workgroup) + strlen(OS_id) +
|
strlen(workgroup) + strlen(OS_id) +
|
strlen(client_id));
|
strlen(client_id));
|
|
|
WSET(server->packet, smb_vwv0, 0x00ff);
|
WSET(server->packet, smb_vwv0, 0x00ff);
|
WSET(server->packet, smb_vwv1, 0);
|
WSET(server->packet, smb_vwv1, 0);
|
WSET(server->packet, smb_vwv2, given_max_xmit);
|
WSET(server->packet, smb_vwv2, given_max_xmit);
|
WSET(server->packet, smb_vwv3, 2);
|
WSET(server->packet, smb_vwv3, 2);
|
WSET(server->packet, smb_vwv4, server->pid);
|
WSET(server->packet, smb_vwv4, server->pid);
|
DSET(server->packet, smb_vwv5, server->sesskey);
|
DSET(server->packet, smb_vwv5, server->sesskey);
|
WSET(server->packet, smb_vwv7, passlen + 1);
|
WSET(server->packet, smb_vwv7, passlen + 1);
|
WSET(server->packet, smb_vwv8, 0);
|
WSET(server->packet, smb_vwv8, 0);
|
WSET(server->packet, smb_vwv9, 0);
|
WSET(server->packet, smb_vwv9, 0);
|
|
|
p = SMB_BUF(server->packet);
|
p = SMB_BUF(server->packet);
|
strcpy(p, server->m.password);
|
strcpy(p, server->m.password);
|
p += passlen + 1;
|
p += passlen + 1;
|
strcpy(p, server->m.username);
|
strcpy(p, server->m.username);
|
p += userlen + 1;
|
p += userlen + 1;
|
strcpy(p, workgroup);
|
strcpy(p, workgroup);
|
p += strlen(p) + 1;
|
p += strlen(p) + 1;
|
strcpy(p, OS_id);
|
strcpy(p, OS_id);
|
p += strlen(p) + 1;
|
p += strlen(p) + 1;
|
strcpy(p, client_id);
|
strcpy(p, client_id);
|
} else
|
} else
|
{
|
{
|
smb_setup_header(server, SMBsesssetupX, 10,
|
smb_setup_header(server, SMBsesssetupX, 10,
|
2 + userlen + passlen);
|
2 + userlen + passlen);
|
|
|
WSET(server->packet, smb_vwv0, 0x00ff);
|
WSET(server->packet, smb_vwv0, 0x00ff);
|
WSET(server->packet, smb_vwv1, 0);
|
WSET(server->packet, smb_vwv1, 0);
|
WSET(server->packet, smb_vwv2, given_max_xmit);
|
WSET(server->packet, smb_vwv2, given_max_xmit);
|
WSET(server->packet, smb_vwv3, 2);
|
WSET(server->packet, smb_vwv3, 2);
|
WSET(server->packet, smb_vwv4, server->pid);
|
WSET(server->packet, smb_vwv4, server->pid);
|
DSET(server->packet, smb_vwv5, server->sesskey);
|
DSET(server->packet, smb_vwv5, server->sesskey);
|
WSET(server->packet, smb_vwv7, passlen + 1);
|
WSET(server->packet, smb_vwv7, passlen + 1);
|
WSET(server->packet, smb_vwv8, 0);
|
WSET(server->packet, smb_vwv8, 0);
|
WSET(server->packet, smb_vwv9, 0);
|
WSET(server->packet, smb_vwv9, 0);
|
|
|
p = SMB_BUF(server->packet);
|
p = SMB_BUF(server->packet);
|
strcpy(p, server->m.password);
|
strcpy(p, server->m.password);
|
p += passlen + 1;
|
p += passlen + 1;
|
strcpy(p, server->m.username);
|
strcpy(p, server->m.username);
|
}
|
}
|
|
|
if ((result = smb_request_ok(server, SMBsesssetupX, 3, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBsesssetupX, 3, 0)) < 0)
|
{
|
{
|
DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
|
DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
goto fail;
|
goto fail;
|
}
|
}
|
smb_decode_word(server->packet + 32, &(server->server_uid));
|
smb_decode_word(server->packet + 32, &(server->server_uid));
|
} else
|
} else
|
{
|
{
|
server->max_xmit = 0;
|
server->max_xmit = 0;
|
server->maxmux = 0;
|
server->maxmux = 0;
|
server->maxvcs = 0;
|
server->maxvcs = 0;
|
server->blkmode = 0;
|
server->blkmode = 0;
|
server->sesskey = 0;
|
server->sesskey = 0;
|
}
|
}
|
|
|
/* Fine! We have a connection, send a tcon message. */
|
/* Fine! We have a connection, send a tcon message. */
|
|
|
smb_setup_header(server, SMBtcon, 0,
|
smb_setup_header(server, SMBtcon, 0,
|
6 + strlen(server->m.service) +
|
6 + strlen(server->m.service) +
|
strlen(server->m.password) + strlen(dev));
|
strlen(server->m.password) + strlen(dev));
|
|
|
p = SMB_BUF(server->packet);
|
p = SMB_BUF(server->packet);
|
p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
|
p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
|
p = smb_encode_ascii(p, server->m.password, strlen(server->m.password));
|
p = smb_encode_ascii(p, server->m.password, strlen(server->m.password));
|
p = smb_encode_ascii(p, dev, strlen(dev));
|
p = smb_encode_ascii(p, dev, strlen(dev));
|
|
|
if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0)
|
if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0)
|
{
|
{
|
DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
|
DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
goto fail;
|
goto fail;
|
}
|
}
|
DDPRINTK("OK! Managed to set up SMBtcon!\n");
|
DDPRINTK("OK! Managed to set up SMBtcon!\n");
|
|
|
p = SMB_VWV(server->packet);
|
p = SMB_VWV(server->packet);
|
|
|
if (server->protocol <= PROTOCOL_COREPLUS)
|
if (server->protocol <= PROTOCOL_COREPLUS)
|
{
|
{
|
word max_xmit;
|
word max_xmit;
|
|
|
p = smb_decode_word(p, &max_xmit);
|
p = smb_decode_word(p, &max_xmit);
|
server->max_xmit = max_xmit;
|
server->max_xmit = max_xmit;
|
|
|
if (server->max_xmit > given_max_xmit)
|
if (server->max_xmit > given_max_xmit)
|
{
|
{
|
server->max_xmit = given_max_xmit;
|
server->max_xmit = given_max_xmit;
|
}
|
}
|
} else
|
} else
|
{
|
{
|
p += 2;
|
p += 2;
|
}
|
}
|
|
|
p = smb_decode_word(p, &server->tid);
|
p = smb_decode_word(p, &server->tid);
|
|
|
/* Ok, everything is fine. max_xmit does not include */
|
/* Ok, everything is fine. max_xmit does not include */
|
/* the TCP-SMB header of 4 bytes. */
|
/* the TCP-SMB header of 4 bytes. */
|
server->max_xmit += 4;
|
server->max_xmit += 4;
|
|
|
DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
|
DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
|
|
|
/* Now make a new packet with the correct size. */
|
/* Now make a new packet with the correct size. */
|
smb_vfree(server->packet);
|
smb_vfree(server->packet);
|
server->packet = NULL;
|
server->packet = NULL;
|
|
|
server->packet = smb_vmalloc(server->max_xmit);
|
server->packet = smb_vmalloc(server->max_xmit);
|
if (server->packet == NULL)
|
if (server->packet == NULL)
|
{
|
{
|
printk("smb_proc_connect: No memory left in end of "
|
printk("smb_proc_connect: No memory left in end of "
|
"connection phase :-(\n");
|
"connection phase :-(\n");
|
smb_dont_catch_keepalive(server);
|
smb_dont_catch_keepalive(server);
|
goto fail;
|
goto fail;
|
}
|
}
|
server->packet_size = server->max_xmit;
|
server->packet_size = server->max_xmit;
|
|
|
DPRINTK("smb_proc_connect: Normal exit\n");
|
DPRINTK("smb_proc_connect: Normal exit\n");
|
return 0;
|
return 0;
|
|
|
fail:
|
fail:
|
server->state = CONN_INVALID;
|
server->state = CONN_INVALID;
|
return result;
|
return result;
|
}
|
}
|
|
|
/* smb_proc_reconnect: server->packet is allocated with
|
/* smb_proc_reconnect: server->packet is allocated with
|
server->max_xmit bytes if and only if we return >= 0 */
|
server->max_xmit bytes if and only if we return >= 0 */
|
int
|
int
|
smb_proc_connect(struct smb_server *server)
|
smb_proc_connect(struct smb_server *server)
|
{
|
{
|
int result;
|
int result;
|
smb_lock_server(server);
|
smb_lock_server(server);
|
|
|
result = smb_proc_reconnect(server);
|
result = smb_proc_reconnect(server);
|
|
|
if ((result < 0) && (server->packet != NULL))
|
if ((result < 0) && (server->packet != NULL))
|
{
|
{
|
smb_vfree(server->packet);
|
smb_vfree(server->packet);
|
server->packet = NULL;
|
server->packet = NULL;
|
}
|
}
|
smb_unlock_server(server);
|
smb_unlock_server(server);
|
return result;
|
return result;
|
}
|
}
|
|
|
int
|
int
|
smb_proc_disconnect(struct smb_server *server)
|
smb_proc_disconnect(struct smb_server *server)
|
{
|
{
|
smb_setup_header_exclusive(server, SMBtdis, 0, 0);
|
smb_setup_header_exclusive(server, SMBtdis, 0, 0);
|
return smb_request_ok_unlock(server, SMBtdis, 0, 0);
|
return smb_request_ok_unlock(server, SMBtdis, 0, 0);
|
}
|
}
|
|
|