patch-2.1.58 linux/fs/smbfs/proc.c
Next file: linux/fs/smbfs/sock.c
Previous file: linux/fs/smbfs/inode.c
Back to the patch index
Back to the overall index
- Lines: 1530
- Date:
Sun Oct 12 10:17:05 1997
- Orig file:
v2.1.57/linux/fs/smbfs/proc.c
- Orig date:
Tue Sep 23 16:48:49 1997
diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
@@ -5,6 +5,8 @@
* Copyright (C) 1997 by Volker Lendecke
*
* 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
+ * 28/09/97 - Fixed smb_d_path [now smb_build_path()] to be non-recursive
+ * by Riccardo Facchetti
*/
#include <linux/config.h>
@@ -16,6 +18,8 @@
#include <linux/malloc.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <linux/dcache.h>
+#include <linux/dirent.h>
#include <asm/uaccess.h>
#include <asm/string.h>
@@ -33,8 +37,7 @@
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
-static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc);
-void smb_close_socket(struct smb_sb_info *);
+extern void smb_renew_times(struct dentry *);
static inline int
min(int a, int b)
@@ -64,6 +67,17 @@
}
}
+static void reverse_string(char *buf, int len) {
+ char c;
+ char *end = buf+len-1;
+
+ while(buf < end) {
+ c = *buf;
+ *(buf++) = *end;
+ *(end--) = c;
+ }
+}
+
/*****************************************************************************/
/* */
/* Encoding/Decoding section */
@@ -85,30 +99,55 @@
}
/*
- * Return the server for the specified dentry
- * N.B. Make this a #define in the smb header
+ * smb_build_path: build the path to entry and name storing it in buf.
+ * The path returned will have the trailing '\0'.
*/
-static struct smb_sb_info * server_from_dentry(struct dentry * dentry)
+static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
{
- return &dentry->d_sb->u.smbfs_sb;
-}
+ char *path = buf;
-static int smb_d_path(struct dentry * entry, char * buf)
-{
+ if (entry == NULL)
+ goto test_name_and_out;
+
+ /*
+ * If IS_ROOT, we have to do no walking at all.
+ */
if (IS_ROOT(entry)) {
- *buf = '\\';
- return 1;
- } else {
- int len = smb_d_path(entry->d_parent, buf);
-
- buf += len;
- if (len > 1) {
- *buf++ = '\\';
- len++;
- }
- memcpy(buf, entry->d_name.name, entry->d_name.len);
- return len + entry->d_name.len;
+ *(path++) = '\\';
+ if (name != NULL)
+ goto name_and_out;
+ goto out;
}
+
+ /*
+ * Build the path string walking the tree backward from end to ROOT
+ * and store it in reversed order [see reverse_string()]
+ */
+ for (;;) {
+ memcpy(path, entry->d_name.name, entry->d_name.len);
+ reverse_string(path, entry->d_name.len);
+ path += entry->d_name.len;
+
+ *(path++) = '\\';
+
+ entry = entry->d_parent;
+
+ if (IS_ROOT(entry))
+ break;
+ }
+
+ reverse_string(buf, path-buf);
+
+test_name_and_out:
+ if (name != NULL) {
+ *(path++) = '\\';
+name_and_out:
+ memcpy(path, name->name, name->len);
+ path += name->len;
+ }
+out:
+ *(path++) = '\0';
+ return (path-buf);
}
static char *smb_encode_path(struct smb_sb_info *server, char *buf,
@@ -116,15 +155,7 @@
{
char *start = buf;
- if (dir != NULL)
- buf += smb_d_path(dir, buf);
-
- if (name != NULL) {
- *buf++ = '\\';
- memcpy(buf, name->name, name->len);
- buf += name->len;
- *buf++ = 0;
- }
+ buf += smb_build_path(dir, name, buf);
if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
str_upper(start);
@@ -249,6 +280,55 @@
(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
}
+/*
+ * Returns the maximum read or write size for the current packet size
+ * and max_xmit value.
+ * N.B. Since this value is usually computed before locking the server,
+ * the server's packet size must never be decreased!
+ */
+static int
+smb_get_xmitsize(struct smb_sb_info *server, int overhead)
+{
+ int size = server->packet_size;
+
+ /*
+ * Start with the smaller of packet size and max_xmit ...
+ */
+ if (size > server->opt.max_xmit)
+ size = server->opt.max_xmit;
+ return size - overhead;
+}
+
+/*
+ * Calculate the maximum read size
+ */
+int
+smb_get_rsize(struct smb_sb_info *server)
+{
+ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_get_rsize: packet=%d, xmit=%d, size=%d\n",
+server->packet_size, server->opt.max_xmit, size);
+#endif
+ return size;
+}
+
+/*
+ * Calculate the maximum write size
+ */
+int
+smb_get_wsize(struct smb_sb_info *server)
+{
+ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_get_wsize: packet=%d, xmit=%d, size=%d\n",
+server->packet_size, server->opt.max_xmit, size);
+#endif
+ return size;
+}
+
static int
smb_errno(int errcls, int error)
{
@@ -365,38 +445,6 @@
up(&(server->sem));
}
-/* smb_request_ok: We expect the server to be locked. Then we do the
- request and check the answer completely. When smb_request_ok
- returns 0, you can be quite sure that everything went well. When
- the answer is <=0, the returned number is a valid unix errno. */
-
-static int
-smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
-{
- int result = 0;
-
- s->rcls = 0;
- s->err = 0;
-
- if (smb_request(s) < 0)
- {
- pr_debug("smb_request failed\n");
- result = -EIO;
- } else if (smb_valid_packet(s->packet) != 0)
- {
- pr_debug("not a valid packet!\n");
- result = -EIO;
- } else if (s->rcls != 0)
- {
- result = -smb_errno(s->rcls, s->err);
- } else if (smb_verify(s->packet, command, wct, bcc) != 0)
- {
- pr_debug("smb_verify failed\n");
- result = -EIO;
- }
- return result;
-}
-
/*
* smb_retry: This function should be called when smb_request_ok has
indicated an error. If the error was indicated because the
@@ -409,6 +457,8 @@
static int
smb_retry(struct smb_sb_info *server)
{
+ struct wait_queue wait = { current, NULL };
+ unsigned long timeout;
int result = 0;
if (server->state != CONN_INVALID)
@@ -423,31 +473,94 @@
goto out;
}
- printk("smb_retry: signalling process %d\n", server->conn_pid);
kill_proc(server->conn_pid, SIGUSR1, 0);
+#if 0
server->conn_pid = 0;
+#endif
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_retry: signalled pid %d, waiting for new connection\n",
+server->conn_pid);
+#endif
/*
- * Block here until we get a new connection.
- * N.B. This needs to be fixed ... we should wait in an
- * interruptible sleep for CONN_VALID.
+ * Wait here for a new connection.
*/
- printk("smb_retry: blocking for new connection\n");
- smb_lock_server(server);
+ timeout = jiffies + 10*HZ;
+ add_wait_queue(&server->wait, &wait);
+ while (1)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + HZ;
+ if (server->state != CONN_INVALID)
+ break;
+ if (jiffies > timeout)
+ {
+ printk("smb_retry: timed out, try again later\n");
+ break;
+ }
+ if (signal_pending(current))
+ {
+ printk("smb_retry: caught signal\n");
+ break;
+ }
+ schedule();
+ }
+ remove_wait_queue(&server->wait, &wait);
+ current->timeout = 0;
+ current->state = TASK_RUNNING;
if (server->state == CONN_VALID)
{
- printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+#ifdef SMBFS_PARANOIA
+printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+#endif
result = 1;
}
+
out:
return result;
}
+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
+{
+ int result = 0;
+
+ s->rcls = 0;
+ s->err = 0;
+
+ /* Make sure we have a connection */
+ if (s->state != CONN_VALID && !smb_retry(s))
+ {
+ result = -EIO;
+ } else if (smb_request(s) < 0)
+ {
+ pr_debug("smb_request failed\n");
+ result = -EIO;
+ } else if (smb_valid_packet(s->packet) != 0)
+ {
+ pr_debug("not a valid packet!\n");
+ result = -EIO;
+ } else if (s->rcls != 0)
+ {
+ result = -smb_errno(s->rcls, s->err);
+ } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+ {
+ pr_debug("smb_verify failed\n");
+ result = -EIO;
+ }
+ return result;
+}
+
/*
* This is called with the server locked after a successful smb_newconn().
* It installs the new connection pid, sets server->state to CONN_VALID,
- * and unlocks the server.
+ * and wakes up the process waiting for the new connection.
* N.B. The first call is made without locking the server -- need to fix!
*/
int
@@ -458,21 +571,24 @@
error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_offerconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }
server->conn_pid = current->pid;
server->state = CONN_VALID;
- printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
+ wake_up_interruptible(&server->wait);
+#ifdef SMBFS_PARANOIA
+printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
+#endif
error = 0;
- /*
- * The initial call may be made without the server locked?
- */
out:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_offerconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
return error;
}
@@ -488,15 +604,21 @@
error = -EBADF;
if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd]))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out;
error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
- goto out_unlock;
+ goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_newconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }
/*
* Make sure the old socket is closed
@@ -507,23 +629,15 @@
server->sock_file = filp;
smb_catch_keepalive(server);
server->opt = *opt;
- pr_debug("smb_newconn: protocol = %d\n", server->opt.protocol);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_newconn: protocol=%d, max_xmit=%d\n",
+server->opt.protocol, server->opt.max_xmit);
+#endif
server->generation += 1;
error = 0;
out:
return error;
-
- /*
- * Unlock now if an error occurred.
- */
-out_unlock:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_newconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
- goto out;
}
/* smb_setup_header: We completely set up the packet. You only have to
@@ -536,6 +650,10 @@
__u8 *p = server->packet;
__u8 *buf = server->packet;
+if (xmit_len > server->packet_size)
+printk("smb_setup_header: Aieee, xmit len > packet! len=%d, size=%d\n",
+xmit_len, server->packet_size);
+
p = smb_encode_smb_length(p, xmit_len - 4);
*p++ = 0xff;
@@ -577,92 +695,106 @@
}
/*
- * We're called with the server locked, and we leave it that way. We
- * try maximum permissions.
+ * We're called with the server locked, and we leave it that way.
+ * Set the permissions to be consistent with the desired access.
*/
static int
-smb_proc_open(struct smb_sb_info *server, struct dentry *dir)
+smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
{
struct inode *ino = dir->d_inode;
+ int mode, read_write = 0x42, read_only = 0x40;
int error;
char *p;
+ mode = read_write;
+#if 0
+ if (!(wish & (O_WRONLY | O_RDWR)))
+ mode = read_only;
+#endif
+
retry:
p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x42); /* read/write */
+ WSET(server->packet, smb_vwv0, mode);
WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
*p++ = 4;
p = smb_encode_path(server, p, dir, NULL);
smb_setup_bcc(server, p);
- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ error = smb_request_ok(server, SMBopen, 7, 0);
+ if (error != 0)
{
if (smb_retry(server))
goto retry;
- if ((error != -EACCES) && (error != -ETXTBSY)
- && (error != -EROFS))
- goto out;
-
- p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x40); /* read only */
- WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
- *p++ = 4;
- p = smb_encode_path(server, p, dir, NULL);
- smb_setup_bcc(server, p);
-
- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ if (mode == read_write &&
+ (error == -EACCES || error == -ETXTBSY || error == -EROFS))
{
- if (smb_retry(server))
- goto retry;
- goto out;
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_open: %s/%s open failed, error=%d, retrying R/O\n",
+dir->d_parent->d_name.name, dir->d_name.name, error);
+#endif
+ mode = read_only;
+ goto retry;
}
}
/* We should now have data in vwv[0..6]. */
ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0);
ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1);
+ /* smb_vwv2 has mtime */
+ /* smb_vwv4 has size */
ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6);
ino->u.smbfs_i.access &= 3;
+ /* N.B. Suppose the open failed?? */
ino->u.smbfs_i.open = server->generation;
- pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access);
-out:
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_open: error=%d, access=%d\n", error, ino->u.smbfs_i.access);
+#endif
return error;
}
int
-smb_open(struct dentry *dir, int wish)
+smb_open(struct dentry *dentry, int wish)
{
- struct inode *i = dir->d_inode;
+ struct inode *i = dentry->d_inode;
int result;
- result = -EIO;
+ result = -ENOENT;
if (!i)
{
printk("smb_open: no inode for dentry %s/%s\n",
- dir->d_parent->d_name.name, dir->d_name.name);
+ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
}
/*
- * If the inode is already open, we don't need to lock the server.
+ * Note: If the caller holds an active dentry and the file is
+ * currently open, we can be sure that the file isn't about
+ * to be closed. (See smb_close_dentry() below.)
*/
if (!smb_is_open(i))
{
struct smb_sb_info *server = SMB_SERVER(i);
smb_lock_server(server);
- result = smb_proc_open(server, dir);
+ result = 0;
+ if (!smb_is_open(i))
+ result = smb_proc_open(server, dentry, wish);
smb_unlock_server(server);
if (result)
{
- printk("smb_open: %s/%s open failed, result=%d\n",
- dir->d_parent->d_name.name, dir->d_name.name,
- result);
+#ifdef SMBFS_PARANOIA
+printk("smb_open: %s/%s open failed, result=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, result);
+#endif
goto out;
}
+ /*
+ * A successful open means the path is still valid ...
+ */
+ smb_renew_times(dentry);
}
result = -EACCES;
@@ -679,15 +811,35 @@
/* We're called with the server locked */
-static int smb_proc_close(struct smb_sb_info *server,
- __u16 fileid, __u32 mtime)
+static int
+smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime)
{
smb_setup_header(server, SMBclose, 3, 0);
WSET(server->packet, smb_vwv0, fileid);
- DSET(server->packet, smb_vwv1, mtime);
+ DSET(server->packet, smb_vwv1, utc2local(mtime));
return smb_request_ok(server, SMBclose, 0, 0);
}
+/*
+ * Called with the server locked
+ */
+static int
+smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
+{
+ int result = 0;
+ if (smb_is_open(ino))
+ {
+ /*
+ * We clear the open flag in advance, in case another
+ * process observes the value while we block below.
+ */
+ ino->u.smbfs_i.open = 0;
+ result = smb_proc_close(server, ino->u.smbfs_i.fileid,
+ ino->i_mtime);
+ }
+ return result;
+}
+
int
smb_close(struct inode *ino)
{
@@ -697,39 +849,66 @@
{
struct smb_sb_info *server = SMB_SERVER(ino);
smb_lock_server(server);
- result = smb_proc_close(server, ino->u.smbfs_i.fileid,
- ino->i_mtime);
+ result = smb_proc_close_inode(server, ino);
smb_unlock_server(server);
- ino->u.smbfs_i.open = 0;
}
return result;
}
+/*
+ * This routine is called from dput() when d_count is going to 0.
+ * We use this to close the file so that cached dentries don't
+ * keep too many files open.
+ *
+ * There are some tricky race conditions here: the dentry may go
+ * back into use while we're closing the file, and we don't want
+ * the new user to be confused as to the open status.
+ */
+void
+smb_close_dentry(struct dentry * dentry)
+{
+ struct inode *ino = dentry->d_inode;
+
+ if (ino)
+ {
+ if (smb_is_open(ino))
+ {
+ struct smb_sb_info *server = SMB_SERVER(ino);
+ smb_lock_server(server);
+ /*
+ * Check whether the dentry is back in use.
+ */
+ if (dentry->d_count <= 1)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_close_dentry: closing %s/%s, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+ smb_proc_close_inode(server, ino);
+ }
+ smb_unlock_server(server);
+ }
+ }
+ /* Consider dropping negative dentries? */
+#if 0
+ else
+ d_drop(dentry);
+#endif
+}
+
/* In smb_proc_read and smb_proc_write we do not retry, because the
file-id would not be valid after a reconnection. */
-/* smb_proc_read: fs indicates if it should be copied with
- copy_to_user. */
-
int
-smb_proc_read(struct inode *ino, off_t offset, long count, char *data)
+smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
{
struct smb_sb_info *server = SMB_SERVER(ino);
__u16 returned_count, data_len;
char *buf;
int result;
- struct dentry * dentry;
-
- if (!ino || !(dentry = ino->u.smbfs_i.dentry))
- {
- printk("smb_proc_read: no inode!\n");
- return -EIO;
- }
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
-
- /* Achtung! Do not refer to the cached packet after the request! */
buf = server->packet;
WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(buf, smb_vwv1, count);
@@ -739,10 +918,6 @@
result = smb_request_ok(server, SMBread, 5, -1);
if (result < 0)
goto out;
-#if 0
-printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet);
-#endif
returned_count = WVAL(server->packet, smb_vwv0);
buf = SMB_BUF(server->packet);
@@ -758,6 +933,11 @@
result = data_len;
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);
+#endif
smb_unlock_server(server);
return result;
}
@@ -766,10 +946,17 @@
smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
{
struct smb_sb_info *server = SMB_SERVER(ino);
- int res = 0;
+ int result;
__u8 *p;
smb_lock_server(server);
+#if SMBFS_DEBUG_VERBOSE
+{struct dentry * dentry = ino->u.smbfs_i.dentry;
+printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+count, offset, server->packet_size);
+}
+#endif
p = smb_setup_header(server, SMBwrite, 5, count + 3);
WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(server->packet, smb_vwv1, count);
@@ -780,12 +967,12 @@
WSET(p, 0, count);
memcpy(p+2, data, count);
- if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
- res = WVAL(server->packet, smb_vwv0);
+ if ((result = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+ result = WVAL(server->packet, smb_vwv0);
smb_unlock_server(server);
- return res;
+ return result;
}
int
@@ -1003,50 +1190,46 @@
smb_init_dirent(server, fattr);
fattr->attr = aDIR;
fattr->f_ino = 1;
+ fattr->f_mtime = CURRENT_TIME;
smb_finish_dirent(server, fattr);
}
static __u8 *
-smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
- struct smb_dirent *entry)
+smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct dirent *entry)
{
- smb_init_dirent(server, &(entry->attr));
+ int len;
p += SMB_STATUS_SIZE; /* reserved (search_status) */
- entry->attr.attr = *p;
- entry->attr.f_mtime = entry->attr.f_atime = entry->attr.f_ctime =
- date_dos2unix(WVAL(p, 1), WVAL(p, 3));
- entry->attr.f_size = DVAL(p, 5);
- entry->len = strlen(p + 9);
- if (entry->len > 12)
- {
- entry->len = 12;
- }
- memcpy(entry->name, p + 9, entry->len);
- entry->name[entry->len] = '\0';
- while (entry->len > 2)
- {
- /* Pathworks fills names with spaces */
- entry->len -= 1;
- if (entry->name[entry->len] == ' ')
- {
- entry->name[entry->len] = '\0';
- }
+ len = strlen(p + 9);
+ if (len > 12)
+ {
+ len = 12;
}
+ memcpy(entry->d_name, p + 9, len);
+#ifdef SMBFS_TRIM_BLANKS
+ /*
+ * Trim trailing blanks for Pathworks servers
+ */
+ while (len > 2 && entry->d_name[len-1] == ' ')
+ len--;
+#endif
+ entry->d_name[len] = '\0';
+ entry->d_reclen = len;
+ entry->d_ino = 0; /* no inode number available */
+
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->name);
+ str_upper(entry->d_name);
break;
case SMB_CASE_LOWER:
- str_lower(entry->name);
+ str_lower(entry->d_name);
break;
default:
break;
}
pr_debug("smb_decode_dirent: name = %s\n", entry->name);
- smb_finish_dirent(server, &(entry->attr));
return p + 22;
}
@@ -1056,50 +1239,43 @@
static int
smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *entry)
+ void *cachep)
{
char *p;
- char *buf;
- int error;
int result;
- int i;
- int first, total_count;
- struct smb_dirent *current_entry;
+ int i, first, entries_seen, entries;
+ int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
__u16 bcc;
__u16 count;
char status[SMB_STATUS_SIZE];
- int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
-
static struct qstr mask = { "*.*", 3, 0 };
- pr_debug("SMB call readdir %d @ %d\n", cache_size, fpos);
+ pr_debug("smb_proc_readdir_short: %d @ %d\n", cache_size, fpos);
smb_lock_server(server);
+ /* N.B. We need to reinitialize the cache to restart */
retry:
+ smb_init_dircache(cachep);
first = 1;
- total_count = 0;
- current_entry = entry;
+ entries = 0;
+ entries_seen = 2; /* implicit . and .. */
while (1)
{
- buf = server->packet;
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(server->packet, smb_vwv0, entries_asked);
+ WSET(server->packet, smb_vwv1, aDIR);
+ *p++ = 4;
if (first == 1)
{
- p = smb_setup_header(server, SMBsearch, 2, 0);
- WSET(buf, smb_vwv0, entries_asked);
- WSET(buf, smb_vwv1, aDIR);
- *p++ = 4;
p = smb_encode_path(server, p, dir, &mask);
*p++ = 5;
WSET(p, 0, 0);
p += 2;
+ first = 0;
} else
{
- p = smb_setup_header(server, SMBsearch, 2, 0);
- WSET(buf, smb_vwv0, entries_asked);
- WSET(buf, smb_vwv1, aDIR);
- *p++ = 4;
*p++ = 0;
*p++ = 5;
WSET(p, 0, SMB_STATUS_SIZE);
@@ -1110,39 +1286,25 @@
smb_setup_bcc(server, p);
- if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
+ result = smb_request_ok(server, SMBsearch, 1, -1);
+ if (result < 0)
{
- if ((server->rcls == ERRDOS)
- && (server->err == ERRnofiles))
- {
- result = total_count - fpos;
- goto unlock_return;
- } else
- {
- if (smb_retry(server))
- {
- goto retry;
- }
- result = error;
- goto unlock_return;
- }
+ if ((server->rcls == ERRDOS) &&
+ (server->err == ERRnofiles))
+ break;
+ if (smb_retry(server))
+ goto retry;
+ goto unlock_return;
}
p = SMB_VWV(server->packet);
count = WVAL(p, 0);
- bcc = WVAL(p, 2);
-
- first = 0;
-
if (count <= 0)
- {
- result = total_count - fpos;
- goto unlock_return;
- }
+ break;
+
+ result = -EIO;
+ bcc = WVAL(p, 2);
if (bcc != count * SMB_DIRINFO_SIZE + 3)
- {
- result = -EIO;
goto unlock_return;
- }
p += 7;
/* Read the last entry into the status field. */
@@ -1155,102 +1317,125 @@
for (i = 0; i < count; i++)
{
- if (total_count < fpos)
- {
- p += SMB_DIRINFO_SIZE;
- pr_debug("smb_proc_readdir: skipped entry.\n");
- pr_debug(" total_count = %d\n"
- " i = %d, fpos = %d\n",
- total_count, i, fpos);
- } else if (total_count >= fpos + cache_size)
+ struct dirent this_ent, *entry = &this_ent;
+
+ p = smb_decode_dirent(server, p, entry);
+ if (entries_seen == 2 && entry->d_name[0] == '.')
{
- result = total_count - fpos;
- goto unlock_return;
- } else
+ if (entry->d_reclen == 1)
+ continue;
+ if (entry->d_name[1] == '.' &&
+ entry->d_reclen == 2)
+ continue;
+ }
+ if (entries_seen >= fpos)
{
- p = smb_decode_dirent(server, p,
- current_entry);
- current_entry->f_pos = total_count;
- pr_debug("smb_proc_readdir: entry->f_pos = "
- "%u\n", entry->f_pos);
- current_entry += 1;
+ pr_debug("smb_proc_readdir: fpos=%u\n",
+ entries_seen);
+ smb_add_to_cache(cachep, entry, entries_seen);
+ entries++;
}
- total_count += 1;
+#ifdef SMBFS_DEBUG_VERBOSE
+else
+printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n",
+entries_seen, i, fpos);
+#endif
+ entries_seen++;
}
}
- unlock_return:
+ result = entries;
+
+ unlock_return:
smb_unlock_server(server);
return result;
}
-/* interpret a long filename structure - this is mostly guesses at the
- 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
- is used by OS/2. */
-
+/*
+ * Interpret a long filename structure using the specified info level:
+ * level 1 -- Win NT, Win 95, OS/2
+ * level 2 -- OS/2
+ * level 259 -- File name and length only, Win NT, Win 95
+ * level 260 -- Win NT, Win 95
+ * There seem to be numerous inconsistencies and bugs in implementation.
+ */
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p,
- struct smb_dirent *entry, int level)
+ struct dirent *entry, int level)
{
char *result;
unsigned int len;
- smb_init_dirent(server, &(entry->attr));
+ /*
+ * SMB doesn't have a concept of inode numbers ...
+ */
+ entry->d_ino = 0;
switch (level)
{
- /* We might add more levels later... */
case 1:
- entry->attr.f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
- entry->attr.f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
- entry->attr.f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
- entry->attr.f_size = DVAL(p, 16);
- entry->attr.attr = *(p+24);
- /*
- * Achtung, lengths can go up to 255
- */
len = *((unsigned char *) p + 26);
- entry->len = len;
- strncpy(entry->name, p + 27, len);
- entry->name[len] = '\0';
+ entry->d_reclen = len;
+ strncpy(entry->d_name, p + 27, len);
+ entry->d_name[len] = '\0';
result = p + 28 + len;
break;
+ case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */
+ /*
+ * This info level returns just the file name and length,
+ * which is all we need right now.
+ */
+ result = p + DVAL(p, 0);
+ /* DVAL(p, 4) should be resume key? Seems to be 0 .. */
+ len = DVAL(p, 8);
+ if (len > 255)
+ len = 255;
+ strncpy(entry->d_name, p + 12, len);
+ /*
+ * Kludge alert: Win NT 4.0 adds a trailing null byte and
+ * counts it in the name length, but Win 95 doesn't. Hence
+ * we test for a trailing null and decrement the length ...
+ */
+ if (len && entry->d_name[len-1] == '\0')
+ len--;
+ entry->d_name[len] = '\0';
+ entry->d_reclen = len;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_decode_long_dirent: info 259, len=%d, name=%s\n",
+len, entry->d_name);
+#endif
+ break;
+
default:
- printk("smb_decode: Unknown long filename format %d\n", level);
+ printk("smb_decode_long_dirent: Unknown level %d\n", level);
result = p + WVAL(p, 0);
}
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->name);
+ str_upper(entry->d_name);
break;
case SMB_CASE_LOWER:
- str_lower(entry->name);
+ str_lower(entry->d_name);
break;
default:
break;
}
- smb_finish_dirent(server, &(entry->attr));
-
return result;
}
static int
smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *cache)
+ void *cachep)
{
- /* NT uses 260, OS/2 uses 2. Both accept 1. */
- const int info_level = 1;
+ /* Both NT and OS/2 accept info level 1 (but see note below). */
+ int info_level = 1;
const int max_matches = 512;
- char *p;
- char *lastname;
- unsigned lastname_len;
- int i;
+ char *p, *mask, *lastname;
int first, entries, entries_seen;
unsigned char *resp_data = NULL;
@@ -1260,36 +1445,45 @@
__u16 command;
- int result;
-
- int ff_resume_key = 0;
+ int ff_resume_key = 0; /* this isn't being used */
int ff_searchcount = 0;
int ff_eos = 0;
int ff_lastname = 0;
int ff_dir_handle = 0;
int loop_count = 0;
+ int mask_len, i, result;
- char param[SMB_MAXPATHLEN + 2 + 12]; /* too long for the stack! */
- int mask_len;
- char *mask = &(param[12]);
-
+ char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */
static struct qstr star = { "*", 1, 0 };
- mask_len = smb_encode_path(server, mask, dir, &star) - mask;
-
- mask[mask_len] = 0;
- mask[mask_len + 1] = 0;
-
- pr_debug("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
- cache_size, fpos, mask);
+ /*
+ * 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. (Win 95 uses this too?)
+ */
+ if (server->opt.protocol >= SMB_PROTOCOL_NT1)
+ info_level = 259;
smb_lock_server(server);
retry:
-
+ /*
+ * Encode the initial path
+ */
+ mask = &(param[12]);
+ mask_len = smb_encode_path(server, mask, dir, &star) - mask;
first = 1;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: starting fpos=%d, mask=%s\n", fpos, mask);
+#endif
+ /*
+ * We must reinitialize the dircache when retrying.
+ */
+ smb_init_dircache(cachep);
entries = 0;
entries_seen = 2;
+ ff_eos = 0;
while (ff_eos == 0)
{
@@ -1301,6 +1495,7 @@
entries = -EIO;
break;
}
+
if (first != 0)
{
command = TRANSACT2_FINDFIRST;
@@ -1314,10 +1509,11 @@
} else
{
command = TRANSACT2_FINDNEXT;
- pr_debug("hand=0x%X resume=%d ff_lastnm=%d mask=%s\n",
- ff_dir_handle, ff_resume_key, ff_lastname,
- mask);
- WSET(param, 0, ff_dir_handle);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: handle=0x%X, resume=%d, lastname=%d, mask=%s\n",
+ff_dir_handle, ff_resume_key, ff_lastname, mask);
+#endif
+ WSET(param, 0, ff_dir_handle); /* search handle */
WSET(param, 2, max_matches); /* max count */
WSET(param, 4, info_level);
DSET(param, 6, ff_resume_key); /* ff_resume_key */
@@ -1335,7 +1531,7 @@
}
result = smb_trans2_request(server, command,
- 0, NULL, 12 + mask_len + 2, param,
+ 0, NULL, 12 + mask_len + 1, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
@@ -1343,10 +1539,13 @@
{
if (smb_retry(server))
{
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_readdir_long: error=%d, retrying\n", result);
+#endif
goto retry;
}
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: trans2_request error=%d\n", result);
+printk("smb_proc_readdir_long: error=%d, breaking\n", result);
#endif
entries = result;
break;
@@ -1354,13 +1553,17 @@
if (server->rcls != 0)
{
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: error, rcls=%d, err=%d\n",
+printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n",
server->rcls, server->err);
#endif
- /* Why isn't this considered an error? */
- /* entries = -EIO; */
+ entries = -smb_errno(server->rcls, server->err);
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)
@@ -1380,86 +1583,90 @@
{
break;
}
- /* point to the data bytes */
- p = resp_data;
/* we might need the lastname for continuations */
- lastname = "";
- lastname_len = 0;
+ mask_len = 0;
if (ff_lastname > 0)
{
- ff_resume_key = 0;
- lastname = p + ff_lastname;
+ lastname = resp_data + ff_lastname;
switch (info_level)
{
case 260:
- lastname_len = resp_data_len - ff_lastname;
+ if (ff_lastname < resp_data_len)
+ mask_len = resp_data_len - ff_lastname;
break;
case 1:
- lastname_len = *((unsigned char *) lastname++);
+ /* Win NT 4.0 doesn't set the length byte */
+ lastname++;
+ if (ff_lastname + 2 < resp_data_len)
+ mask_len = strlen(lastname);
break;
}
+ /*
+ * Update the mask string for the next message.
+ */
+ if (mask_len > 255)
+ mask_len = 255;
+ if (mask_len)
+ strncpy(mask, lastname, mask_len);
+ ff_resume_key = 0;
}
- lastname_len = min(lastname_len, 256);
- strncpy(mask, lastname, lastname_len);
- mask[lastname_len] = '\0';
-
+ mask[mask_len] = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: new mask, len=%d@%d, mask=%s\n",
+mask_len, ff_lastname, mask);
+#endif
/* Now we are ready to parse smb directory entries. */
+ /* point to the data bytes */
+ p = resp_data;
for (i = 0; i < ff_searchcount; i++)
{
- struct smb_dirent *entry = &(cache[entries]);
+ struct dirent this_ent, *entry = &this_ent;
- p = smb_decode_long_dirent(server, p,
- entry, info_level);
+ p = smb_decode_long_dirent(server, p, entry,
+ info_level);
pr_debug("smb_readdir_long: got %s\n", entry->name);
- if ((entry->name[0] == '.')
- && ((entry->name[1] == '\0')
- || ((entry->name[1] == '.')
- && (entry->name[2] == '\0'))))
+ /* ignore . and .. from the server */
+ if (entries_seen == 2 && entry->d_name[0] == '.')
{
- /* ignore . and .. from the server */
- continue;
+ if (entry->d_reclen == 1)
+ continue;
+ if (entry->d_name[1] == '.' &&
+ entry->d_reclen == 2)
+ continue;
}
if (entries_seen >= fpos)
{
- entry->f_pos = entries_seen;
+ smb_add_to_cache(cachep, entry, entries_seen);
entries += 1;
}
- entries_seen += 1;
- if (entries < cache_size)
- continue;
-
- /* cache is full */
- goto finished;
+ entries_seen++;
}
- pr_debug("received %d entries (eos=%d resume=%d)\n",
- ff_searchcount, ff_eos, ff_resume_key);
-
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: received %d entries, eos=%d, resume=%d\n",
+ff_searchcount, ff_eos, ff_resume_key);
+#endif
first = 0;
}
- finished:
smb_unlock_server(server);
return entries;
}
int
-smb_proc_readdir(struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *entry)
+smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
{
struct smb_sb_info *server;
server = server_from_dentry(dir);
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
- return smb_proc_readdir_long(server, dir, fpos, cache_size,
- entry);
+ return smb_proc_readdir_long(server, dir, fpos, cachep);
else
- return smb_proc_readdir_short(server, dir, fpos, cache_size,
- entry);
+ return smb_proc_readdir_short(server, dir, fpos, cachep);
}
static int
@@ -1498,7 +1705,6 @@
smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
struct qstr *name, struct smb_fattr *attr)
{
- char param[SMB_MAXPATHLEN + 20];
char *p;
int result;
@@ -1506,29 +1712,34 @@
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! */
+ smb_lock_server(server);
+
+ retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
p = smb_encode_path(server, param + 6, dir, name);
- smb_lock_server(server);
- retry:
result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
0, NULL, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
-
- if (server->rcls != 0)
- {
- result = -smb_errno(server->rcls, server->err);
- goto out;
- }
if (result < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
+ if (server->rcls != 0)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n",
+¶m[6], result, server->rcls, server->err);
+#endif
+ result = -smb_errno(server->rcls, server->err);
+ goto out;
+ }
result = -ENOENT;
if (resp_data_len < 22)
goto out;
@@ -1553,15 +1764,19 @@
struct smb_fattr *fattr)
{
struct smb_sb_info *server;
- int result = -1;
+ int result;
server = server_from_dentry(dir);
smb_init_dirent(server, fattr);
+ /*
+ * N.B. Why would we want to fall back to xxx_core on error?
+ * If the file doesn't exist (a very common case), the core
+ * protocol won't find it either.
+ */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_getattr_trans2(server, dir, name, fattr);
-
- if (result < 0)
+ else
result = smb_proc_getattr_core(server, dir, name, fattr);
smb_finish_dirent(server, fattr);
@@ -1605,8 +1820,6 @@
smb_proc_setattr_trans2(struct smb_sb_info *server,
struct dentry *dir, struct smb_fattr *fattr)
{
- char param[SMB_MAXPATHLEN + 20];
- char data[26];
char *p;
int result;
@@ -1614,7 +1827,12 @@
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! */
+ char data[26];
+ smb_lock_server(server);
+
+ retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
p = smb_encode_path(server, param + 6, dir, NULL);
@@ -1627,22 +1845,20 @@
WSET(data, 20, fattr->attr);
WSET(data, 22, 0);
- smb_lock_server(server);
- retry:
result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
26, data, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
-
- if (server->rcls != 0)
- {
- smb_unlock_server(server);
- return -smb_errno(server->rcls, server->err);
- }
if (result < 0)
+ {
if (smb_retry(server))
goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ result = -smb_errno(server->rcls, server->err);
+out:
smb_unlock_server(server);
return 0;
}
@@ -1651,12 +1867,14 @@
smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *fattr)
{
- int result = -1;
+ int result;
+ /*
+ * N.B. Why would we want to fall back to xxx_core on error?
+ */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_setattr_trans2(server, dir, fattr);
-
- if (result < 0)
+ else
result = smb_proc_setattr_core(server, dir, fattr);
return result;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov