summaryrefslogtreecommitdiffstats
path: root/fs/smbfs/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smbfs/proc.c')
-rw-r--r--fs/smbfs/proc.c245
1 files changed, 158 insertions, 87 deletions
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index 05a1ac4f1..34f9f969f 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -9,10 +9,11 @@
* by Riccardo Facchetti
*/
-#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/dcache.h>
@@ -531,7 +532,8 @@ server->conn_pid);
if (server->state == CONN_VALID)
{
#ifdef SMBFS_PARANOIA
-printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+printk("smb_retry: new pid=%d, generation=%d\n",
+server->conn_pid, server->generation);
#endif
result = 1;
}
@@ -620,52 +622,43 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_newconn: fd=%d, pid=%d\n", opt->fd, current->pid);
#endif
- error = -EBADF;
- if (opt->fd < 0 || opt->fd >= NR_OPEN)
- goto out;
- if (!(filp = current->files->fd[opt->fd]))
- goto out;
- if (!smb_valid_socket(filp->f_dentry->d_inode))
- goto out;
-
- error = -EACCES;
- if ((current->uid != server->mnt->mounted_uid) && !suser())
- goto out;
-
/*
* Make sure we don't already have a pid ...
*/
error = -EINVAL;
if (server->conn_pid)
- {
- printk("SMBFS: invalid ioctl call\n");
goto out;
- }
- server->conn_pid = current->pid;
-#ifdef SMBFS_PARANOIA
-if (server->sock_file)
-printk("smb_newconn: old socket not closed!\n");
-#endif
+ error = -EACCES;
+ if (current->uid != server->mnt->mounted_uid && !suser())
+ goto out;
+
+ error = -EBADF;
+ filp = fget(opt->fd);
+ if (!filp)
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out_putf;
- filp->f_count += 1;
server->sock_file = filp;
+ server->conn_pid = current->pid;
smb_catch_keepalive(server);
server->opt = *opt;
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_newconn: protocol=%d, max_xmit=%d\n",
-server->opt.protocol, server->opt.max_xmit);
-#endif
server->generation += 1;
server->state = CONN_VALID;
-#ifdef SMBFS_PARANOIA
-printk("smb_newconn: state valid, pid=%d\n", server->conn_pid);
-#endif
error = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d\n",
+server->opt.protocol, server->opt.max_xmit, server->conn_pid);
+#endif
out:
wake_up_interruptible(&server->wait);
return error;
+
+out_putf:
+ fput(filp);
+ goto out;
}
/* smb_setup_header: We completely set up the packet. You only have to
@@ -975,7 +968,7 @@ int
smb_close_fileid(struct dentry *dentry, __u16 fileid)
{
struct smb_sb_info *server = server_from_dentry(dentry);
- int result = 0;
+ int result;
smb_lock_server(server);
result = smb_proc_close(server, fileid, CURRENT_TIME);
@@ -987,9 +980,9 @@ smb_close_fileid(struct dentry *dentry, __u16 fileid)
file-id would not be valid after a reconnection. */
int
-smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
+smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data)
{
- struct smb_sb_info *server = SMB_SERVER(ino);
+ struct smb_sb_info *server = server_from_dentry(dentry);
__u16 returned_count, data_len;
char *buf;
int result;
@@ -997,7 +990,7 @@ smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
buf = server->packet;
- WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid);
+ WSET(buf, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
@@ -1022,29 +1015,27 @@ smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
out:
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_proc_read: file %s/%s, count=%d, result=%d\n",
-((struct dentry *) ino->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *) ino->u.smbfs_i.dentry)->d_name.name, count, result);
+dentry->d_parent->d_name.name, dentry->d_name.name, count, result);
#endif
smb_unlock_server(server);
return result;
}
int
-smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
+smb_proc_write(struct dentry *dentry, off_t offset, int count, const char *data)
{
- struct smb_sb_info *server = SMB_SERVER(ino);
+ struct smb_sb_info *server = server_from_dentry(dentry);
int result;
__u8 *p;
- smb_lock_server(server);
#if SMBFS_DEBUG_VERBOSE
printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
-((struct dentry *)ino->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *)ino->u.smbfs_i.dentry)->d_name.name,
+dentry->d_parent->d_name.name, dentry->d_name.name,
count, offset, server->packet_size);
#endif
+ smb_lock_server(server);
p = smb_setup_header(server, SMBwrite, 5, count + 3);
- WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid);
+ WSET(server->packet, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid);
WSET(server->packet, smb_vwv1, count);
DSET(server->packet, smb_vwv2, offset);
WSET(server->packet, smb_vwv4, 0);
@@ -1270,6 +1261,9 @@ smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
/*
* Note that we are now returning the name as a reference to avoid
* an extra copy, and that the upper/lower casing is done in place.
+ *
+ * Bugs Noted:
+ * (1) Pathworks servers may pad the name with extra spaces.
*/
static __u8 *
smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
@@ -1442,10 +1436,6 @@ entries_seen, i, fpos);
*
* Bugs Noted:
* (1) Win NT 4.0 appends a null byte to names and counts it in the length!
- * (2) When using Info Level 1 Win NT 4.0 truncates directory listings
- * for certain patterns of names and/or lengths. The breakage pattern is
- * completely reproducible and can be toggled by the addition of a single
- * file to the directory. (E.g. echo hi >foo breaks, rm -f foo works.)
*/
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p,
@@ -1509,24 +1499,29 @@ p, len, entry->name);
return result;
}
+/*
+ * Bugs Noted:
+ * (1) When using Info Level 1 Win NT 4.0 truncates directory listings
+ * for certain patterns of names and/or lengths. The breakage pattern
+ * is completely reproducible and can be toggled by the creation of a
+ * single file. (E.g. echo hi >foo breaks, rm -f foo works.)
+ */
static int
smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
void *cachep)
{
+ char *p, *mask, *lastname, *param = server->temp_buf;
+ __u16 command;
+ int first, entries, entries_seen;
+
/* Both NT and OS/2 accept info level 1 (but see note below). */
int info_level = 1;
const int max_matches = 512;
- char *p, *mask, *lastname;
- int first, entries, entries_seen;
-
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
-
- __u16 command;
-
int ff_resume_key = 0; /* this isn't being used */
int ff_searchcount = 0;
int ff_eos = 0;
@@ -1534,17 +1529,16 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
int ff_dir_handle = 0;
int loop_count = 0;
int mask_len, i, result;
-
- char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */
static struct qstr star = { "*", 1, 0 };
/*
* Check whether to change the info level. There appears to be
* a bug in Win NT 4.0's handling of info level 1, whereby it
* truncates the directory scan for certain patterns of files.
- * Hence we use level 259 for NT. (And Win 95 as well ...)
+ * Hence we use level 259 for NT.
*/
- if (server->opt.protocol >= SMB_PROTOCOL_NT1)
+ if (server->opt.protocol >= SMB_PROTOCOL_NT1 &&
+ !(server->mnt->version & SMB_FIX_WIN95))
info_level = 259;
smb_lock_server(server);
@@ -1553,7 +1547,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
/*
* Encode the initial path
*/
- mask = &(param[12]);
+ mask = param + 12;
mask_len = smb_encode_path(server, mask, dir, &star) - mask;
first = 1;
#ifdef SMBFS_DEBUG_VERBOSE
@@ -1637,17 +1631,12 @@ printk("smb_proc_readdir_long: error=%d, breaking\n", result);
if (server->rcls != 0)
{
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n",
-server->rcls, server->err);
+printk("smb_proc_readdir_long: name=%s, entries=%d, rcls=%d, err=%d\n",
+mask, entries, server->rcls, server->err);
#endif
entries = -smb_errno(server);
break;
}
-#ifdef SMBFS_PARANOIA
-if (resp_data + resp_data_len > server->packet + server->packet_size)
-printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n",
-resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size);
-#endif
/* parse out some important return info */
if (first != 0)
@@ -1753,6 +1742,94 @@ smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
}
/*
+ * This version uses the trans2 TRANSACT2_FINDFIRST message
+ * to get the attribute data.
+ * Note: called with the server locked.
+ *
+ * Bugs Noted:
+ */
+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;
+ __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;
+
+retry:
+ mask_len = smb_encode_path(server, mask, dentry, NULL) - mask;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_ff: name=%s, len=%d\n", mask, mask_len);
+#endif
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, 1); /* max count */
+ WSET(param, 4, 1); /* close after this call */
+ WSET(param, 6, 1); /* info_level */
+ DSET(param, 8, 0);
+
+ result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
+ 0, NULL, 12 + mask_len + 1, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ {
+ result = -smb_errno(server);
+#ifdef SMBFS_PARANOIA
+if (result != -ENOENT)
+printk("smb_proc_getattr_ff: error for %s, rcls=%d, err=%d\n",
+mask, server->rcls, server->err);
+#endif
+ goto out;
+ }
+ /* Make sure we got enough data ... */
+ result = -EINVAL;
+ if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_getattr_ff: bad result for %s, len=%d, count=%d\n",
+mask, resp_data_len, WVAL(resp_param, 2));
+#endif
+ goto out;
+ }
+
+ /*
+ * Decode the response into the fattr ...
+ */
+ date = WVAL(resp_data, 0);
+ time = WVAL(resp_data, 2);
+ fattr->f_ctime = date_dos2unix(date, time);
+
+ date = WVAL(resp_data, 4);
+ time = WVAL(resp_data, 6);
+ fattr->f_atime = date_dos2unix(date, time);
+
+ date = WVAL(resp_data, 8);
+ time = WVAL(resp_data, 10);
+ fattr->f_mtime = date_dos2unix(date, time);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n",
+mask, date, time, fattr->f_mtime);
+#endif
+ fattr->f_size = DVAL(resp_data, 12);
+ /* ULONG allocation size */
+ fattr->attr = WVAL(resp_data, 20);
+ result = 0;
+
+out:
+ return result;
+}
+
+/*
* Note: called with the server locked.
*/
static int
@@ -1799,15 +1876,14 @@ static int
smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *attr)
{
- char *p;
- int result;
+ char *p, *param = server->temp_buf;
__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;
- char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */
+ int result;
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
@@ -1887,11 +1963,17 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
* Win 95 is painfully slow at returning trans2 getattr info,
* so we provide the SMB_FIX_OLDATTR option switch.
*/
- if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
- !(server->mnt->version & SMB_FIX_OLDATTR))
- result = smb_proc_getattr_trans2(server, dir, fattr);
- else
+ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) {
+ if (server->mnt->version & SMB_FIX_OLDATTR)
+ goto core_attr;
+ if (server->mnt->version & SMB_FIX_DIRATTR)
+ result = smb_proc_getattr_ff(server, dir, fattr);
+ else
+ result = smb_proc_getattr_trans2(server, dir, fattr);
+ } else {
+ core_attr:
result = smb_proc_getattr_core(server, dir, fattr);
+ }
smb_finish_dirent(server, fattr);
@@ -2023,14 +2105,12 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
struct dentry *dir, struct smb_fattr *fattr)
{
__u16 date, time;
- char *p;
- int result;
-
+ 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 param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */
+ int result;
char data[26];
retry:
@@ -2082,7 +2162,8 @@ out:
* Bugs Noted:
* (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message
* with info level 1 (INFO_STANDARD).
- * (2) Under the core protocol apparently the only way to set the
+ * (2) Win 95 seems not to support setting directory timestamps.
+ * (3) Under the core protocol apparently the only way to set the
* timestamp is to open and close the file.
*/
int
@@ -2092,7 +2173,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
struct inode *inode = dentry->d_inode;
int result;
-#ifdef SMBFS_DEBUG_TIMESTAMP
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_proc_settime: setting %s/%s, open=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
#endif
@@ -2127,16 +2208,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
}
}
-#if 1 /* temporary */
- if (result)
- {
-printk("smb_proc_settime: %s/%s failed, open=%d, res=%d, rcls=%d, err=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode),
-result, server->rcls, server->err);
- /* squash errors for now */
- result = 0;
- }
-#endif
smb_unlock_server(server);
return result;
}