summaryrefslogtreecommitdiffstats
path: root/fs/smbfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-03-09 20:33:35 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-03-09 20:33:35 +0000
commit116674acc97ba75a720329996877077d988443a2 (patch)
tree6a3f2ff0b612ae2ee8a3f3509370c9e6333a53b3 /fs/smbfs
parent71118c319fcae4a138f16e35b4f7e0a6d53ce2ca (diff)
Merge with Linux 2.4.2.
Diffstat (limited to 'fs/smbfs')
-rw-r--r--fs/smbfs/ChangeLog11
-rw-r--r--fs/smbfs/Makefile1
-rw-r--r--fs/smbfs/cache.c408
-rw-r--r--fs/smbfs/dir.c197
-rw-r--r--fs/smbfs/file.c13
-rw-r--r--fs/smbfs/inode.c30
-rw-r--r--fs/smbfs/proc.c386
-rw-r--r--fs/smbfs/sock.c55
8 files changed, 638 insertions, 463 deletions
diff --git a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog
index a57b1cc67..6ceae2331 100644
--- a/fs/smbfs/ChangeLog
+++ b/fs/smbfs/ChangeLog
@@ -1,5 +1,14 @@
ChangeLog for smbfs.
+2001-02-10 Urban Widmark <urban@teststation.com>
+
+ * dir.c: replace non-bigmem safe cache with cache code from ncpfs
+ and fix some other bigmem bugs in smbfs.
+ * inode.c: root dentry not properly initialized
+ * proc.c, sock.c: adjust max parameters & max data to follow max_xmit
+ lots of servers were having find_next trouble with this.
+ * proc.c: use documented write method of truncating (NetApp fix)
+
2000-08-14 Urban Widmark <urban@svenskatest.se>
* dir.c: support case sensitive shares
@@ -31,7 +40,7 @@ ChangeLog for smbfs.
* *.c: replace ugly #ifdef's with less ugly debug macros.
-2000-01-?? cpg@aladdin.de
+2000-01-03 Christian Groessler <cpg@aladdin.de>
* proc.c: added posix semantics for unlink
diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
index db76849be..16e8ceb15 100644
--- a/fs/smbfs/Makefile
+++ b/fs/smbfs/Makefile
@@ -20,5 +20,6 @@ EXTRA_CFLAGS += -DSMBFS_PARANOIA
#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE
#EXTRA_CFLAGS += -DDEBUG_SMB_MALLOC
#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP
+#EXTRA_CFLAGS += -Werror
include $(TOPDIR)/Rules.make
diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c
index aa85680ac..e3e9c044f 100644
--- a/fs/smbfs/cache.c
+++ b/fs/smbfs/cache.c
@@ -4,8 +4,7 @@
* Copyright (C) 1997 by Bill Hawes
*
* Routines to support directory cacheing using the page cache.
- * Right now this only works for smbfs, but will be generalized
- * for use with other filesystems.
+ * This cache code is almost directly taken from ncpfs.
*
* Please add a note about your changes to smbfs in the ChangeLog file.
*/
@@ -22,271 +21,222 @@
#include "smb_debug.h"
-
-static inline struct address_space *
-get_cache_inode(struct cache_head *cachep)
-{
- return page_cache_entry((unsigned long) cachep)->mapping;
-}
-
/*
- * Try to reassemble the old dircache. If we fail - set ->valid to 0.
- * In any case, get at least the page at offset 0 (with ->valid==0 if
- * the old one didn't make it, indeed).
+ * Force the next attempt to use the cache to be a timeout.
+ * If we can't find the page that's fine, it will cause a refresh.
*/
-struct cache_head *
-smb_get_dircache(struct dentry * dentry)
+void
+smb_invalid_dir_cache(struct inode * dir)
{
- struct address_space * mapping = &dentry->d_inode->i_data;
- struct cache_head * cachep = NULL;
- struct page *page;
+ struct smb_sb_info *server = server_from_inode(dir);
+ union smb_dir_cache *cache = NULL;
+ struct page *page = NULL;
- page = find_lock_page(mapping, 0);
- if (!page) {
- /* Sorry, not even page 0 around */
- page = grab_cache_page(mapping, 0);
- if (!page)
- goto out;
- cachep = kmap(page);
- memset((char*)cachep, 0, PAGE_SIZE);
+ page = grab_cache_page(&dir->i_data, 0);
+ if (!page)
goto out;
- }
- cachep = kmap(page);
- if (cachep->valid) {
- /*
- * OK, at least the page 0 survived and seems to be promising.
- * Let's try to reassemble the rest.
- */
- struct cache_index * index = cachep->index;
- unsigned long offset;
- int i;
- for (offset = 0, i = 0; i < cachep->pages; i++, index++) {
- offset += PAGE_SIZE;
- page = find_lock_page(mapping,offset>>PAGE_CACHE_SHIFT);
- if (!page) {
- /* Alas, poor Yorick */
- cachep->valid = 0;
- goto out;
- }
- index->block = kmap(page);
- }
- }
-out:
- return cachep;
-}
-
-/*
- * Unlock and release the data blocks.
- */
-static void
-smb_free_cache_blocks(struct cache_head * cachep)
-{
- struct cache_index * index = cachep->index;
- struct page * page;
- int i;
+ if (!Page_Uptodate(page))
+ goto out_unlock;
- VERBOSE("freeing %d blocks\n", cachep->pages);
- for (i = 0; i < cachep->pages; i++, index++) {
- if (!index->block)
- continue;
- page = page_cache_entry((unsigned long) index->block);
- index->block = NULL;
- kunmap(page);
- UnlockPage(page);
- page_cache_release(page);
- }
-}
+ cache = kmap(page);
+ cache->head.time = jiffies - SMB_MAX_AGE(server);
-/*
- * Unlocks and releases the dircache.
- */
-void
-smb_free_dircache(struct cache_head * cachep)
-{
- struct page *page;
- VERBOSE("freeing cache\n");
- smb_free_cache_blocks(cachep);
- page = page_cache_entry((unsigned long) cachep);
kunmap(page);
+ SetPageUptodate(page);
+out_unlock:
UnlockPage(page);
page_cache_release(page);
+out:
}
/*
- * Initializes the dircache. We release any existing data blocks,
- * and then clear the cache_head structure.
+ * Mark all dentries for 'parent' as invalid, forcing them to be re-read
*/
void
-smb_init_dircache(struct cache_head * cachep)
+smb_invalidate_dircache_entries(struct dentry *parent)
{
- VERBOSE("initializing cache, %d blocks\n", cachep->pages);
- smb_free_cache_blocks(cachep);
- memset(cachep, 0, sizeof(struct cache_head));
+ struct smb_sb_info *server = server_from_dentry(parent);
+ struct list_head *next;
+ struct dentry *dentry;
+
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dentry = list_entry(next, struct dentry, d_child);
+ dentry->d_fsdata = NULL;
+ smb_age_dentry(server, dentry);
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
}
-/*
- * Add a new entry to the cache. This assumes that the
- * entries are coming in order and are added to the end.
- */
-void
-smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry,
- off_t fpos)
-{
- struct address_space * mapping = get_cache_inode(cachep);
- struct cache_index * index;
- struct cache_block * block;
- struct page *page;
- unsigned long page_off;
- unsigned int nent, offset, len = entry->len;
- unsigned int needed = len + sizeof(struct cache_entry);
-
- VERBOSE("cache %p, status %d, adding %.*s at %ld\n",
- mapping, cachep->status, entry->len, entry->name, fpos);
+static int
+smb_d_validate(struct dentry *dentry)
+{
+ unsigned long dent_addr = (unsigned long) dentry;
+ unsigned long min_addr = PAGE_OFFSET;
+ unsigned long align_mask = 0x0F;
+ unsigned int len;
+ int valid = 0;
+
+ if (dent_addr < min_addr)
+ goto bad_addr;
+ if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry))
+ goto bad_addr;
+ if ((dent_addr & ~align_mask) != dent_addr)
+ goto bad_align;
+ if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 +
+ sizeof(struct dentry))))
+ goto bad_addr;
/*
- * Don't do anything if we've had an error ...
+ * Looks safe enough to dereference ...
*/
- if (cachep->status)
+ len = dentry->d_name.len;
+ if (len > SMB_MAXPATHLEN)
goto out;
-
- index = &cachep->index[cachep->idx];
- if (!index->block)
- goto get_block;
-
- /* space available? */
- if (needed < index->space) {
- add_entry:
- nent = index->num_entries;
- index->num_entries++;
- index->space -= needed;
- offset = index->space +
- index->num_entries * sizeof(struct cache_entry);
- block = index->block;
- memcpy(&block->cb_data.names[offset], entry->name, len);
- block->cb_data.table[nent].namelen = len;
- block->cb_data.table[nent].offset = offset;
- block->cb_data.table[nent].ino = entry->ino;
- cachep->entries++;
-
- VERBOSE("added entry %.*s, len=%d, pos=%ld, entries=%d\n",
- entry->len, entry->name, len, fpos, cachep->entries);
- return;
- }
/*
- * This block is full ... advance the index.
+ * Note: d_validate doesn't dereference the parent pointer ...
+ * just combines it with the name hash to find the hash chain.
*/
- cachep->idx++;
- if (cachep->idx > NINDEX) /* not likely */
- goto out_full;
- index++;
- /*
- * Get the next cache block. We don't care for its contents.
- */
-get_block:
- cachep->pages++;
- page_off = PAGE_SIZE + (cachep->idx << PAGE_SHIFT);
- page = grab_cache_page(mapping, page_off>>PAGE_CACHE_SHIFT);
- if (page) {
- block = kmap(page);
- index->block = block;
- index->space = PAGE_SIZE;
- goto add_entry;
- }
- /*
- * On failure, just set the return status ...
- */
-out_full:
- cachep->status = -ENOMEM;
+ valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len);
out:
- return;
+ return valid;
+
+bad_addr:
+ printk(KERN_ERR "smb_d_validate: invalid address %lx\n", dent_addr);
+ goto out;
+bad_align:
+ printk(KERN_ERR "smb_d_validate: unaligned address %lx\n", dent_addr);
+ goto out;
}
-int
-smb_find_in_cache(struct cache_head * cachep, off_t pos,
- struct cache_dirent *entry)
+/*
+ * dget, but require that fpos and parent matches what the dentry contains.
+ * dentry is not known to be a valid pointer at entry.
+ */
+struct dentry *
+smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
{
- struct cache_index * index = cachep->index;
- struct cache_block * block;
- unsigned int i, nent, offset = 0;
- off_t next_pos = 2;
-
- VERBOSE("smb_find_in_cache: cache %p, looking for pos=%ld\n",
- cachep, pos);
- for (i = 0; i < cachep->pages; i++, index++)
- {
- if (pos < next_pos)
- break;
- nent = pos - next_pos;
- next_pos += index->num_entries;
- if (pos >= next_pos)
- continue;
- /*
- * The entry is in this block. Note: we return
- * then name as a reference with _no_ null byte.
- */
- block = index->block;
- entry->ino = block->cb_data.table[nent].ino;
- entry->len = block->cb_data.table[nent].namelen;
- offset = block->cb_data.table[nent].offset;
- entry->name = &block->cb_data.names[offset];
+ struct dentry *dent = dentry;
+ struct list_head *next;
+
+ if (smb_d_validate(dent)) {
+ if (dent->d_parent == parent &&
+ (unsigned long)dent->d_fsdata == fpos) {
+ if (!dent->d_inode) {
+ dput(dent);
+ dent = NULL;
+ }
+ return dent;
+ }
+ dput(dent);
+ }
- VERBOSE("found %.*s, len=%d, pos=%ld\n",
- entry->len, entry->name, entry->len, pos);
- break;
+ /* If a pointer is invalid, we search the dentry. */
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dent = list_entry(next, struct dentry, d_child);
+ if ((unsigned long)dent->d_fsdata == fpos) {
+ if (dent->d_inode)
+ dget_locked(dent);
+ else
+ dent = NULL;
+ goto out_unlock;
+ }
+ next = next->next;
}
- return offset;
+ dent = NULL;
+out_unlock:
+ spin_unlock(&dcache_lock);
+ return dent;
}
+
+/*
+ * Create dentry/inode for this file and add it to the dircache.
+ */
int
-smb_refill_dircache(struct cache_head * cachep, struct dentry *dentry)
+smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctrl, struct qstr *qname,
+ struct smb_fattr *entry)
{
- struct inode * inode = dentry->d_inode;
- int result;
+ struct dentry *newdent, *dentry = filp->f_dentry;
+ struct inode *newino, *inode = dentry->d_inode;
+ struct smb_cache_control ctl = *ctrl;
+ int valid = 0;
+ ino_t ino = 0;
+
+ qname->hash = full_name_hash(qname->name, qname->len);
+
+ if (dentry->d_op && dentry->d_op->d_hash)
+ if (dentry->d_op->d_hash(dentry, qname) != 0)
+ goto end_advance;
+
+ newdent = d_lookup(dentry, qname);
+
+ if (!newdent) {
+ newdent = d_alloc(dentry, qname);
+ if (!newdent)
+ goto end_advance;
+ } else
+ memcpy((char *) newdent->d_name.name, qname->name,
+ newdent->d_name.len);
+
+ if (!newdent->d_inode) {
+ smb_renew_times(newdent);
+ entry->f_ino = iunique(inode->i_sb, 2);
+ newino = smb_iget(inode->i_sb, entry);
+ if (newino) {
+ smb_new_dentry(newdent);
+ d_add(newdent, newino);
+ }
+ } else
+ smb_set_inode_attr(newdent->d_inode, entry);
- VERBOSE("smb_refill_dircache: cache %s/%s, blocks=%d\n",
- DENTRY_PATH(dentry), cachep->pages);
- /*
- * Fill the cache, starting at position 2.
- */
-retry:
- inode->u.smbfs_i.cache_valid |= SMB_F_CACHEVALID;
- result = smb_proc_readdir(dentry, 2, cachep);
- if (result < 0)
- {
- PARANOIA("readdir failed, result=%d\n", result);
- goto out;
+ if (newdent->d_inode) {
+ ino = newdent->d_inode->i_ino;
+ newdent->d_fsdata = (void *) ctl.fpos;
+ smb_new_dentry(newdent);
}
- /*
- * Check whether the cache was invalidated while
- * we were doing the scan ...
- */
- if (!(inode->u.smbfs_i.cache_valid & SMB_F_CACHEVALID))
- {
- PARANOIA("cache invalidated, retrying\n");
- goto retry;
+ if (ctl.idx >= SMB_DIRCACHE_SIZE) {
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
+ ctl.cache = NULL;
+ ctl.idx -= SMB_DIRCACHE_SIZE;
+ ctl.ofs += 1;
+ ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
+ if (ctl.page)
+ ctl.cache = kmap(ctl.page);
}
-
- result = cachep->status;
- if (!result)
- {
- cachep->valid = 1;
+ if (ctl.cache) {
+ ctl.cache->dentry[ctl.idx] = newdent;
+ valid = 1;
}
- VERBOSE("cache %s/%s status=%d, entries=%d\n",
- DENTRY_PATH(dentry), cachep->status, cachep->entries);
-out:
- return result;
-}
-
-void
-smb_invalid_dir_cache(struct inode * dir)
-{
- /*
- * Get rid of any unlocked pages, and clear the
- * 'valid' flag in case a scan is in progress.
- */
- invalidate_inode_pages(dir);
- dir->u.smbfs_i.cache_valid &= ~SMB_F_CACHEVALID;
- dir->u.smbfs_i.oldmtime = 0;
+ dput(newdent);
+
+end_advance:
+ if (!valid)
+ ctl.valid = 0;
+ if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
+ if (!ino)
+ ino = find_inode_number(dentry, qname);
+ if (!ino)
+ ino = iunique(inode->i_sb, 2);
+ ctl.filled = filldir(dirent, qname->name, qname->len,
+ filp->f_pos, ino, DT_UNKNOWN);
+ if (!ctl.filled)
+ filp->f_pos += 1;
+ }
+ ctl.fpos += 1;
+ ctl.idx += 1;
+ *ctrl = ctl;
+ return (ctl.valid || !ctl.filled);
}
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 7b62899c1..ba7aa95f1 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -19,8 +19,6 @@
#include "smb_debug.h"
-#define SMBFS_MAX_AGE 5*HZ
-
static int smb_readdir(struct file *, void *, filldir_t);
static int smb_dir_open(struct inode *, struct file *);
@@ -52,27 +50,39 @@ struct inode_operations smb_dir_inode_operations =
setattr: smb_notify_change,
};
+/*
+ * Read a directory, using filldir to fill the dirent memory.
+ * smb_proc_readdir does the actual reading from the smb server.
+ *
+ * The cache code is almost directly taken from ncpfs
+ */
static int
smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_inode;
- struct cache_head *cachep;
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ union smb_dir_cache *cache = NULL;
+ struct smb_cache_control ctl;
+ struct page *page = NULL;
int result;
+ ctl.page = NULL;
+ ctl.cache = NULL;
+
VERBOSE("reading %s/%s, f_pos=%d\n",
DENTRY_PATH(dentry), (int) filp->f_pos);
result = 0;
- switch ((unsigned int) filp->f_pos)
- {
+ switch ((unsigned int) filp->f_pos) {
case 0:
if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0)
goto out;
filp->f_pos = 1;
+ /* fallthrough */
case 1:
if (filldir(dirent, "..", 2, 1,
- dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+ dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
goto out;
filp->f_pos = 2;
}
@@ -83,61 +93,119 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
result = smb_revalidate_inode(dentry);
if (result)
goto out;
- /*
- * Get the cache pointer ...
- */
- result = -EIO;
- cachep = smb_get_dircache(dentry);
- if (!cachep)
- goto out;
- /*
- * Make sure the cache is up-to-date.
- *
- * To detect changes on the server we refill on each "new" access.
- *
- * Directory mtime would be nice to use for finding changes,
- * unfortunately some servers (NT4) doesn't update on local changes.
- */
- if (!cachep->valid || filp->f_pos == 2)
- {
- result = smb_refill_dircache(cachep, dentry);
- if (result)
- goto out_free;
- }
- result = 0;
+ page = grab_cache_page(&dir->i_data, 0);
+ if (!page)
+ goto read_really;
- while (1)
- {
- struct cache_dirent this_dirent, *entry = &this_dirent;
+ ctl.cache = cache = kmap(page);
+ ctl.head = cache->head;
+
+ if (!Page_Uptodate(page) || !ctl.head.eof) {
+ VERBOSE("%s/%s, page uptodate=%d, eof=%d\n",
+ DENTRY_PATH(dentry), Page_Uptodate(page),ctl.head.eof);
+ goto init_cache;
+ }
+
+ if (filp->f_pos == 2) {
+ if (jiffies - ctl.head.time >= SMB_MAX_AGE(server))
+ goto init_cache;
- if (!smb_find_in_cache(cachep, filp->f_pos, entry))
- break;
/*
- * Check whether to look up the inode number.
+ * N.B. ncpfs checks mtime of dentry too here, we don't.
+ * 1. common smb servers do not update mtime on dir changes
+ * 2. it requires an extra smb request
+ * (revalidate has the same timeout as ctl.head.time)
+ *
+ * Instead smbfs invalidates its own cache on local changes
+ * and remote changes are not seen until timeout.
*/
- if (!entry->ino) {
- struct qstr qname;
- /* N.B. Make cache_dirent name a qstr! */
- qname.name = entry->name;
- qname.len = entry->len;
- entry->ino = find_inode_number(dentry, &qname);
- if (!entry->ino)
- entry->ino = iunique(dentry->d_sb, 2);
- }
-
- if (filldir(dirent, entry->name, entry->len,
- filp->f_pos, entry->ino, DT_UNKNOWN) < 0)
- break;
- filp->f_pos += 1;
}
- /*
- * Release the dircache.
- */
-out_free:
- smb_free_dircache(cachep);
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+
+ ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2);
+ ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE;
+ ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE;
+
+ for (;;) {
+ if (ctl.ofs != 0) {
+ ctl.page = find_lock_page(&dir->i_data, ctl.ofs);
+ if (!ctl.page)
+ goto invalid_cache;
+ ctl.cache = kmap(ctl.page);
+ if (!Page_Uptodate(ctl.page))
+ goto invalid_cache;
+ }
+ while (ctl.idx < SMB_DIRCACHE_SIZE) {
+ struct dentry *dent;
+ int res;
+
+ dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx],
+ dentry, filp->f_pos);
+ if (!dent)
+ goto invalid_cache;
+
+ res = filldir(dirent, dent->d_name.name,
+ dent->d_name.len, filp->f_pos,
+ dent->d_inode->i_ino, DT_UNKNOWN);
+ dput(dent);
+ if (res)
+ goto finished;
+ filp->f_pos += 1;
+ ctl.idx += 1;
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.idx = 0;
+ ctl.ofs += 1;
+ }
+invalid_cache:
+ if (ctl.page) {
+ kunmap(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.cache = cache;
+init_cache:
+ smb_invalidate_dircache_entries(dentry);
+ ctl.head.time = jiffies;
+ ctl.head.eof = 0;
+ ctl.fpos = 2;
+ ctl.ofs = 0;
+ ctl.idx = SMB_DIRCACHE_START;
+ ctl.filled = 0;
+ ctl.valid = 1;
+read_really:
+ result = smb_proc_readdir(filp, dirent, filldir, &ctl);
+ if (ctl.idx == -1)
+ goto invalid_cache; /* retry */
+ ctl.head.end = ctl.fpos - 1;
+ ctl.head.eof = ctl.valid;
+finished:
+ if (page) {
+ cache->head = ctl.head;
+ kunmap(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ page_cache_release(page);
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
out:
return result;
}
@@ -204,16 +272,17 @@ static struct dentry_operations smbfs_dentry_operations_case =
static int
smb_lookup_validate(struct dentry * dentry, int flags)
{
+ struct smb_sb_info *server = server_from_dentry(dentry);
struct inode * inode = dentry->d_inode;
unsigned long age = jiffies - dentry->d_time;
int valid;
/*
* The default validation is based on dentry age:
- * we believe in dentries for 5 seconds. (But each
+ * we believe in dentries for a few seconds. (But each
* successful server lookup renews the timestamp.)
*/
- valid = (age <= SMBFS_MAX_AGE);
+ valid = (age <= SMB_MAX_AGE(server));
#ifdef SMBFS_DEBUG_VERBOSE
if (!valid)
VERBOSE("%s/%s not valid, age=%lu\n",
@@ -286,6 +355,22 @@ smb_delete_dentry(struct dentry * dentry)
}
/*
+ * Initialize a new dentry
+ */
+void
+smb_new_dentry(struct dentry *dentry)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+
+ if (server->mnt->flags & SMB_MOUNT_CASE)
+ dentry->d_op = &smbfs_dentry_operations_case;
+ else
+ dentry->d_op = &smbfs_dentry_operations;
+ dentry->d_time = jiffies;
+}
+
+
+/*
* Whenever a lookup succeeds, we know the parent directories
* are all valid, so we want to update the dentry timestamps.
* N.B. Move this to dcache?
@@ -441,6 +526,7 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
if (!d_unhashed(dentry))
goto out;
+ smb_invalid_dir_cache(dir);
error = smb_proc_rmdir(dentry);
out:
@@ -457,6 +543,7 @@ smb_unlink(struct inode *dir, struct dentry *dentry)
*/
smb_close(dentry->d_inode);
+ smb_invalid_dir_cache(dir);
error = smb_proc_unlink(dentry);
if (!error)
smb_renew_times(dentry);
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index 0a95735fd..dfc370f20 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -13,7 +13,7 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
@@ -38,7 +38,7 @@ smb_fsync(struct file *file, struct dentry * dentry, int datasync)
static int
smb_readpage_sync(struct dentry *dentry, struct page *page)
{
- char *buffer = page_address(page);
+ char *buffer = kmap(page);
unsigned long offset = page->index << PAGE_CACHE_SHIFT;
int rsize = smb_get_rsize(server_from_dentry(dentry));
int count = PAGE_SIZE;
@@ -76,6 +76,7 @@ smb_readpage_sync(struct dentry *dentry, struct page *page)
result = 0;
io_error:
+ kunmap(page);
UnlockPage(page);
return result;
}
@@ -89,8 +90,6 @@ smb_readpage(struct file *file, struct page *page)
int error;
struct dentry *dentry = file->f_dentry;
- DEBUG1("readpage %p\n", page_address(page));
-
get_page(page);
error = smb_readpage_sync(dentry, page);
put_page(page);
@@ -105,7 +104,7 @@ static int
smb_writepage_sync(struct inode *inode, struct page *page,
unsigned long offset, unsigned int count)
{
- u8 *buffer = page_address(page) + offset;
+ char *buffer = kmap(page) + offset;
int wsize = smb_get_wsize(server_from_inode(inode));
int result, written = 0;
@@ -139,8 +138,10 @@ smb_writepage_sync(struct inode *inode, struct page *page,
inode->i_mtime = inode->i_atime = CURRENT_TIME;
if (offset > inode->i_size)
inode->i_size = offset;
- inode->u.smbfs_i.cache_valid |= SMB_F_LOCALWRITE;
+ inode->u.smbfs_i.flags |= SMB_F_LOCALWRITE;
} while (count);
+
+ kunmap(page);
return written ? written : result;
}
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index e502fb60b..40ee12566 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -16,7 +16,7 @@
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/dcache.h>
@@ -43,7 +43,6 @@
static void smb_delete_inode(struct inode *);
static void smb_put_super(struct super_block *);
static int smb_statfs(struct super_block *, struct statfs *);
-static void smb_set_inode_attr(struct inode *, struct smb_fattr *);
static struct super_operations smb_sops =
{
@@ -110,7 +109,7 @@ smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr)
fattr->attr |= aRONLY;
}
-static void
+void
smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
{
inode->i_mode = fattr->f_mode;
@@ -125,8 +124,7 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
* Don't change the size and mtime/atime fields
* if we're writing to the file.
*/
- if (!(inode->u.smbfs_i.cache_valid & SMB_F_LOCALWRITE))
- {
+ if (!(inode->u.smbfs_i.flags & SMB_F_LOCALWRITE)) {
inode->i_size = fattr->f_size;
inode->i_mtime = fattr->f_mtime;
inode->i_atime = fattr->f_atime;
@@ -211,6 +209,7 @@ smb_refresh_inode(struct dentry *dentry)
int
smb_revalidate_inode(struct dentry *dentry)
{
+ struct smb_sb_info *s = server_from_dentry(dentry);
struct inode *inode = dentry->d_inode;
time_t last_time;
int error = 0;
@@ -221,8 +220,7 @@ smb_revalidate_inode(struct dentry *dentry)
* the inode will be up-to-date.
*/
lock_kernel();
- if (S_ISREG(inode->i_mode) && smb_is_open(inode))
- {
+ if (S_ISREG(inode->i_mode) && smb_is_open(inode)) {
if (inode->u.smbfs_i.access != SMB_O_RDONLY)
goto out;
}
@@ -230,10 +228,9 @@ smb_revalidate_inode(struct dentry *dentry)
/*
* Check whether we've recently refreshed the inode.
*/
- if (time_before(jiffies, inode->u.smbfs_i.oldmtime + HZ/10))
- {
- VERBOSE("up-to-date, jiffies=%lu, oldtime=%lu\n",
- jiffies, inode->u.smbfs_i.oldmtime);
+ if (time_before(jiffies, inode->u.smbfs_i.oldmtime + SMB_MAX_AGE(s))) {
+ VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n",
+ inode->i_ino, jiffies, inode->u.smbfs_i.oldmtime);
goto out;
}
@@ -243,16 +240,13 @@ smb_revalidate_inode(struct dentry *dentry)
*/
last_time = inode->i_mtime;
error = smb_refresh_inode(dentry);
- if (error || inode->i_mtime != last_time)
- {
+ if (error || inode->i_mtime != last_time) {
VERBOSE("%s/%s changed, old=%ld, new=%ld\n",
DENTRY_PATH(dentry),
(long) last_time, (long) inode->i_mtime);
if (!S_ISDIR(inode->i_mode))
invalidate_inode_pages(inode);
- else
- smb_invalid_dir_cache(inode);
}
out:
unlock_kernel();
@@ -287,6 +281,7 @@ struct option opts[] = {
{ "dir_mode", 1, 0, 'd' },
{ "iocharset", 1, 0, 'i' },
{ "codepage", 1, 0, 'c' },
+ { "ttl", 1, 0, 't' },
{ NULL, 0, 0, 0}
};
@@ -339,6 +334,9 @@ parse_options(struct smb_mount_data_kernel *mnt, char *options)
strncpy(mnt->codepage.remote_name, optarg,
SMB_NLS_MAXNAMELEN);
break;
+ case 't':
+ mnt->ttl = value;
+ break;
default:
printk ("smbfs: Unrecognized mount option %s\n",
optopt);
@@ -437,6 +435,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
SMB_NLS_MAXNAMELEN);
+ mnt->ttl = 1000;
if (ver == SMB_MOUNT_OLDVERSION) {
mnt->version = oldmnt->version;
@@ -483,6 +482,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
+ smb_new_dentry(sb->s_root);
return sb;
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index f0444f97d..9bfdc90a0 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -9,7 +9,7 @@
#include <linux/types.h>
#include <linux/errno.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
@@ -78,6 +78,7 @@ str_upper(char *name, int len)
}
}
+#if 0
static void
str_lower(char *name, int len)
{
@@ -88,6 +89,7 @@ str_lower(char *name, int len)
name++;
}
}
+#endif
/* reverse a string inline. This is used by the dircache walking routines */
static void reverse_string(char *buf, int len)
@@ -358,6 +360,72 @@ date_unix2dos(struct smb_sb_info *server,
*date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);
}
+/* The following are taken from fs/ntfs/util.c */
+
+/*
+ * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
+ * into Unix UTC (based 1970-01-01, in seconds).
+ *
+ * This is very gross because
+ * 1: We must do 64-bit division on a 32-bit machine
+ * 2: We can't use libgcc for long long operations in the kernel
+ * 3: Floating point math in the kernel would corrupt user data
+ */
+static time_t
+smb_ntutc2unixutc(struct smb_sb_info *server, u64 ntutc)
+{
+ const unsigned int D = 10000000;
+ unsigned int H = (unsigned int)(ntutc >> 32);
+ unsigned int L = (unsigned int)ntutc;
+ unsigned int numerator2;
+ unsigned int lowseconds;
+ unsigned int result;
+
+ /*
+ * It is best to subtract 0x019db1ded53e8000 first.
+ * Then the 1601-based date becomes a 1970-based date.
+ */
+ if (L < (unsigned)0xd53e8000) H--;
+ L -= (unsigned)0xd53e8000;
+ H -= (unsigned)0x019db1de;
+
+ /*
+ * Now divide 64-bit numbers on a 32-bit machine :-)
+ * With the subtraction already done, the result fits in 32 bits.
+ * The numerator fits in 56 bits and the denominator fits
+ * in 24 bits, so we can shift by 8 bits to make this work.
+ */
+
+ numerator2 = (H<<8) | (L>>24);
+ result = (numerator2 / D); /* shifted 24 right!! */
+ lowseconds = result << 24;
+
+ numerator2 = ((numerator2-result*D)<<8) | ((L>>16)&0xff);
+ result = (numerator2 / D); /* shifted 16 right!! */
+ lowseconds |= result << 16;
+
+ numerator2 = ((numerator2-result*D)<<8) | ((L>>8)&0xff);
+ result = (numerator2 / D); /* shifted 8 right!! */
+ lowseconds |= result << 8;
+
+ numerator2 = ((numerator2-result*D)<<8) | (L&0xff);
+ result = (numerator2 / D); /* not shifted */
+ lowseconds |= result;
+
+ return lowseconds;
+}
+
+#if 0
+/* Convert the Unix UTC into NT time */
+static u64
+smb_unixutc2ntutc(struct smb_sb_info *server, time_t t)
+{
+ /* Note: timezone conversion is probably wrong. */
+ return ((utc2local(server, t) + (u64)(369*365+89)*24*3600) * 10000000);
+}
+#endif
+
+
/*****************************************************************************/
/* */
/* Support section. */
@@ -421,22 +489,16 @@ fail:
}
/*
- * Returns the maximum read or write size for the current packet size
- * and max_xmit value.
+ * Returns the maximum read or write size for the "payload". Making all of the
+ * packet fit within the negotiated max_xmit size.
+ *
* N.B. Since this value is usually computed before locking the server,
* the server's packet size must never be decreased!
*/
-static int
+static inline 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;
+ return server->opt.max_xmit - overhead;
}
/*
@@ -783,6 +845,23 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
server->opt.protocol, server->opt.max_xmit, server->conn_pid,
server->opt.capabilities);
+ /* Make sure we can fit a message of the negotiated size in our
+ packet buffer. */
+ if (server->opt.max_xmit > server->packet_size) {
+ int len = smb_round_length(server->opt.max_xmit);
+ char *buf = smb_vmalloc(len);
+ if (buf) {
+ server->packet = buf;
+ server->packet_size = len;
+ } else {
+ /* else continue with the too small buffer? */
+ PARANOIA("Failed to allocate new packet buffer: "
+ "max_xmit=%d, packet_size=%d\n",
+ server->opt.max_xmit, server->packet_size);
+ server->opt.max_xmit = server->packet_size;
+ }
+ }
+
out:
#ifdef SMB_RETRY_INTR
wake_up_interruptible(&server->wait);
@@ -1025,7 +1104,7 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
result = smb_proc_close(server, ino->u.smbfs_i.fileid,
ino->i_mtime);
- ino->u.smbfs_i.cache_valid &= ~SMB_F_LOCALWRITE;
+ ino->u.smbfs_i.flags &= ~SMB_F_LOCALWRITE;
/*
* Force a revalidation after closing ... some servers
* don't post the size until the file has been closed.
@@ -1115,7 +1194,7 @@ smb_proc_read(struct inode *inode, off_t offset, int count, char *data)
out:
VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n",
- inode->ino, inode->u.smbfs_i.fileid, count, result);
+ inode->i_ino, inode->u.smbfs_i.fileid, count, result);
smb_unlock_server(server);
return result;
}
@@ -1128,7 +1207,7 @@ smb_proc_write(struct inode *inode, off_t offset, int count, const char *data)
__u8 *p;
VERBOSE("ino=%ld, fileid=%d, count=%d@%ld, packet_size=%d\n",
- inode->ino, inode->u.smbfs_i.fileid, count, offset,
+ inode->i_ino, inode->u.smbfs_i.fileid, count, offset,
server->packet_size);
smb_lock_server(server);
@@ -1355,14 +1434,13 @@ smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length)
smb_lock_server(server);
retry:
- p = smb_setup_header(server, SMBwrite, 5, 0);
+ p = smb_setup_header(server, SMBwrite, 5, 3);
WSET(server->packet, smb_vwv0, fid);
WSET(server->packet, smb_vwv1, 0);
DSET(server->packet, smb_vwv2, length);
WSET(server->packet, smb_vwv4, 0);
- *p++ = 4;
- *p++ = 0;
- smb_setup_bcc(server, p);
+ *p++ = 1;
+ WSET(p, 0, 0);
if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) {
if (smb_retry(server))
@@ -1419,38 +1497,47 @@ 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.
+ * Decode a dirent for old protocols
+ *
+ * qname is filled with the decoded, and possibly translated, name.
+ * fattr receives decoded attributes
*
* Bugs Noted:
* (1) Pathworks servers may pad the name with extra spaces.
*/
-static __u8 *
-smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
- struct cache_dirent *entry)
+static char *
+smb_decode_short_dirent(struct smb_sb_info *server, char *p,
+ struct qstr *qname, struct smb_fattr *fattr)
{
int len;
/*
* SMB doesn't have a concept of inode numbers ...
*/
- entry->ino = 0;
+ smb_init_dirent(server, fattr);
+ fattr->f_ino = 0; /* FIXME: do we need this? */
p += SMB_STATUS_SIZE; /* reserved (search_status) */
- entry->name = p + 9;
- len = strlen(entry->name);
- if (len > 12)
- len = 12;
+ fattr->attr = *p;
+ fattr->f_mtime = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1));
+ fattr->f_size = DVAL(p, 5);
+ fattr->f_ctime = fattr->f_mtime;
+ fattr->f_atime = fattr->f_mtime;
+ qname->name = p + 9;
+ len = strnlen(qname->name, 12);
/*
* Trim trailing blanks for Pathworks servers
*/
- while (len > 2 && entry->name[len-1] == ' ')
+ while (len > 2 && qname->name[len-1] == ' ')
len--;
- entry->len = len;
+ qname->len = len;
+ smb_finish_dirent(server, fattr);
+
+#if 0
/* FIXME: These only work for ascii chars, and recent smbmount doesn't
- allow the flag to be set anyway. Remove? */
+ allow the flag to be set anyway. It kills const. Remove? */
switch (server->opt.case_handling) {
case SMB_CASE_UPPER:
str_upper(entry->name, len);
@@ -1461,24 +1548,31 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
default:
break;
}
+#endif
- entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
- entry->name, len,
+ qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
server->remote_nls, server->local_nls);
- entry->name = server->name_buf;
+ qname->name = server->name_buf;
- DEBUG1("len=%d, name=%.*s\n", entry->len, entry->len, entry->name);
+ DEBUG1("len=%d, name=%.*s\n", qname->len, qname->len, qname->name);
return p + 22;
}
-/* This routine is used to read in directory entries from the network.
- Note that it is for short directory name seeks, i.e.: protocol <
- SMB_PROTOCOL_LANMAN2 */
-
+/*
+ * This routine is used to read in directory entries from the network.
+ * Note that it is for short directory name seeks, i.e.: protocol <
+ * SMB_PROTOCOL_LANMAN2
+ */
static int
-smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
- void *cachep)
+smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
{
+ struct dentry *dir = filp->f_dentry;
+ struct smb_sb_info *server = server_from_dentry(dir);
+ struct qstr qname;
+ struct smb_fattr fattr;
+
unsigned char *p;
int result;
int i, first, entries_seen, entries;
@@ -1489,13 +1583,10 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
static struct qstr mask = { "*.*", 3, 0 };
unsigned char *last_status;
- VERBOSE("%s/%s, pos=%d\n", DENTRY_PATH(dir), fpos);
+ VERBOSE("%s/%s\n", DENTRY_PATH(dir));
smb_lock_server(server);
- /* N.B. We need to reinitialize the cache to restart */
-retry:
- smb_init_dircache(cachep);
first = 1;
entries = 0;
entries_seen = 2; /* implicit . and .. */
@@ -1530,8 +1621,10 @@ retry:
if ((server->rcls == ERRDOS) &&
(server->err == ERRnofiles))
break;
- if (smb_retry(server))
- goto retry;
+ if (smb_retry(server)) {
+ ctl->idx = -1; /* retry */
+ result = 0;
+ }
goto unlock_return;
}
p = SMB_VWV(server->packet);
@@ -1568,23 +1661,18 @@ retry:
/* Now we are ready to parse smb directory entries. */
for (i = 0; i < count; i++) {
- struct cache_dirent this_ent, *entry = &this_ent;
+ p = smb_decode_short_dirent(server, p,
+ &qname, &fattr);
- p = smb_decode_dirent(server, p, entry);
- if (entries_seen == 2 && entry->name[0] == '.') {
- if (entry->len == 1)
+ if (entries_seen == 2 && qname.name[0] == '.') {
+ if (qname.len == 1)
continue;
- if (entry->name[1] == '.' && entry->len == 2)
+ if (qname.name[1] == '.' && qname.len == 2)
continue;
}
- if (entries_seen >= fpos) {
- DEBUG1("fpos=%u\n", entries_seen);
- smb_add_to_cache(cachep, entry, entries_seen);
- entries++;
- } else {
- VERBOSE("skipped, seen=%d, i=%d, fpos=%d\n",
- entries_seen, i, fpos);
- }
+ if (!smb_fill_cache(filp, dirent, filldir, ctl,
+ &qname, &fattr))
+ ; /* stop reading? */
entries_seen++;
}
}
@@ -1600,44 +1688,69 @@ unlock_return:
* level 1 for anything below NT1 protocol
* level 260 for NT1 protocol
*
- * We return a reference to the name string to avoid copying, and perform
- * any needed upper/lower casing in place.
+ * qname is filled with the decoded, and possibly translated, name
+ * fattr receives decoded attributes.
*
* Bugs Noted:
* (1) Win NT 4.0 appends a null byte to names and counts it in the length!
*/
static char *
-smb_decode_long_dirent(struct smb_sb_info *server, char *p,
- struct cache_dirent *entry, int level)
+smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
+ struct qstr *qname, struct smb_fattr *fattr)
{
char *result;
unsigned int len = 0;
+ __u16 date, time;
/*
* SMB doesn't have a concept of inode numbers ...
*/
- entry->ino = 0;
+ smb_init_dirent(server, fattr);
+ fattr->f_ino = 0; /* FIXME: do we need this? */
switch (level) {
case 1:
len = *((unsigned char *) p + 22);
- entry->name = p + 23;
+ qname->name = p + 23;
result = p + 24 + len;
+ date = WVAL(p, 0);
+ time = WVAL(p, 2);
+ fattr->f_ctime = date_dos2unix(server, date, time);
+
+ date = WVAL(p, 4);
+ time = WVAL(p, 6);
+ fattr->f_atime = date_dos2unix(server, date, time);
+
+ date = WVAL(p, 8);
+ time = WVAL(p, 10);
+ fattr->f_mtime = date_dos2unix(server, date, time);
+ fattr->f_size = DVAL(p, 12);
+ /* ULONG allocation size */
+ fattr->attr = WVAL(p, 20);
+
VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
- p, len, len, entry->name);
+ p, len, len, qname->name);
break;
case 260:
result = p + WVAL(p, 0);
len = DVAL(p, 60);
if (len > 255) len = 255;
/* NT4 null terminates */
- entry->name = p + 94;
- if (len && entry->name[len-1] == '\0')
+ qname->name = p + 94;
+ if (len && qname->name[len-1] == '\0')
len--;
+ fattr->f_ctime = smb_ntutc2unixutc(server, LVAL(p, 8));
+ fattr->f_atime = smb_ntutc2unixutc(server, LVAL(p, 16));
+ fattr->f_mtime = smb_ntutc2unixutc(server, LVAL(p, 24));
+ /* change time (32) */
+ fattr->f_size = DVAL(p, 40);
+ /* alloc size (48) */
+ fattr->attr = DVAL(p, 56);
+
VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
- p, len, len, entry->name);
+ p, len, len, qname->name);
break;
default:
PARANOIA("Unknown info level %d\n", level);
@@ -1645,21 +1758,28 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
goto out;
}
+ smb_finish_dirent(server, fattr);
+
+#if 0
+ /* FIXME: These only work for ascii chars, and recent smbmount doesn't
+ allow the flag to be set anyway. Remove? */
switch (server->opt.case_handling) {
case SMB_CASE_UPPER:
- str_upper(entry->name, len);
+ str_upper(qname->name, len);
break;
case SMB_CASE_LOWER:
- str_lower(entry->name, len);
+ str_lower(qname->name, len);
break;
default:
break;
}
+#endif
- entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
- entry->name, len,
+ qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
server->remote_nls, server->local_nls);
- entry->name = server->name_buf;
+ qname->name = server->name_buf;
+
out:
return result;
}
@@ -1682,13 +1802,18 @@ out:
* 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)
+smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
{
- unsigned char *p;
- char *mask, *lastname, *param = server->temp_buf;
+ struct dentry *dir = filp->f_dentry;
+ struct smb_sb_info *server = server_from_dentry(dir);
+ struct qstr qname;
+ struct smb_fattr fattr;
+
+ unsigned char *p, *lastname;
+ char *mask, *param = server->temp_buf;
__u16 command;
- int first, entries, entries_seen;
+ int first, entries_seen;
/* Both NT and OS/2 accept info level 1 (but see note below). */
int info_level = 260;
@@ -1714,7 +1839,6 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
smb_lock_server(server);
-retry:
/*
* Encode the initial path
*/
@@ -1722,17 +1846,13 @@ retry:
mask_len = smb_encode_path(server, mask, dir, &star);
if (mask_len < 0) {
- entries = mask_len;
+ result = mask_len;
goto unlock_return;
}
first = 1;
- VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask);
+ VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask);
- /*
- * We must reinitialize the dircache when retrying.
- */
- smb_init_dircache(cachep);
- entries = 0;
+ result = 0;
entries_seen = 2;
ff_eos = 0;
@@ -1741,7 +1861,7 @@ retry:
if (loop_count > 10) {
printk(KERN_WARNING "smb_proc_readdir_long: "
"Looping in FIND_NEXT??\n");
- entries = -EIO;
+ result = -EIO;
break;
}
@@ -1773,10 +1893,11 @@ retry:
if (result < 0) {
if (smb_retry(server)) {
PARANOIA("error=%d, retrying\n", result);
- goto retry;
+ ctl->idx = -1; /* retry */
+ result = 0;
+ goto unlock_return;
}
PARANOIA("error=%d, breaking\n", result);
- entries = result;
break;
}
@@ -1788,10 +1909,10 @@ retry:
continue;
}
- if (server->rcls != 0) {
- PARANOIA("name=%s, entries=%d, rcls=%d, err=%d\n",
- mask, entries, server->rcls, server->err);
- entries = -smb_errno(server);
+ if (server->rcls != 0) {
+ result = -smb_errno(server);
+ PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n",
+ mask, result, server->rcls, server->err);
break;
}
@@ -1810,41 +1931,44 @@ retry:
if (ff_searchcount == 0)
break;
- /* we might need the lastname for continuations */
+ /*
+ * We might need the lastname for continuations.
+ *
+ * Note that some servers (win95?) point to the filename and
+ * others (NT4, Samba using NT1) to the dir entry. We assume
+ * here that those who do not point to a filename do not need
+ * this info to continue the listing. OS/2 needs this, but it
+ * talks "infolevel 1"
+ */
mask_len = 0;
- if (ff_lastname > 0) {
+ if (info_level == 1 && ff_lastname > 0 &&
+ ff_lastname < resp_data_len) {
lastname = resp_data + ff_lastname;
- switch (info_level) {
- case 260:
- if (ff_lastname < resp_data_len)
- mask_len = resp_data_len - ff_lastname;
- break;
- case 1:
- /* Win NT 4.0 doesn't set the length byte */
- lastname++;
- if (ff_lastname + 2 < resp_data_len)
- mask_len = strlen(lastname);
- break;
- }
+
+ /* lastname points to a length byte */
+ mask_len = *lastname++;
+ if (ff_lastname + 1 + mask_len > resp_data_len)
+ mask_len = resp_data_len-ff_lastname-1;
+
/*
* Update the mask string for the next message.
*/
+ if (mask_len < 0)
+ mask_len = 0;
if (mask_len > 255)
mask_len = 255;
if (mask_len)
strncpy(mask, lastname, mask_len);
}
- mask[mask_len] = 0;
- VERBOSE("new mask, len=%d@%d, mask=%s\n",
- mask_len, ff_lastname, mask);
+ mask_len = strnlen(mask, mask_len);
+ VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n",
+ mask_len, ff_lastname, resp_data_len, mask_len, mask);
/* Now we are ready to parse smb directory entries. */
/* point to the data bytes */
p = resp_data;
for (i = 0; i < ff_searchcount; i++) {
- struct cache_dirent this_ent, *entry = &this_ent;
-
/* make sure we stay within the buffer */
if (p >= resp_data + resp_data_len) {
printk(KERN_ERR "smb_proc_readdir_long: "
@@ -1856,21 +1980,21 @@ retry:
goto unlock_return;
}
- p = smb_decode_long_dirent(server, p, entry,
- info_level);
+ p = smb_decode_long_dirent(server, p, info_level,
+ &qname, &fattr);
/* ignore . and .. from the server */
- if (entries_seen == 2 && entry->name[0] == '.') {
- if (entry->len == 1)
+ if (entries_seen == 2 && qname.name[0] == '.') {
+ if (qname.len == 1)
continue;
- if (entry->name[1] == '.' && entry->len == 2)
+ if (qname.name[1] == '.' && qname.len == 2)
continue;
}
- if (entries_seen >= fpos) {
- smb_add_to_cache(cachep, entry, entries_seen);
- entries += 1;
- }
- entries_seen++;
+
+ if (!smb_fill_cache(filp, dirent, filldir, ctl,
+ &qname, &fattr))
+ ; /* stop reading? */
+ entries_seen++;
}
VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos);
@@ -1881,19 +2005,19 @@ retry:
unlock_return:
smb_unlock_server(server);
- return entries;
+ return result;
}
int
-smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
+smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
{
- struct smb_sb_info *server;
+ struct smb_sb_info *server = server_from_dentry(filp->f_dentry);
- server = server_from_dentry(dir);
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
- return smb_proc_readdir_long(server, dir, fpos, cachep);
+ return smb_proc_readdir_long(filp, dirent, filldir, ctl);
else
- return smb_proc_readdir_short(server, dir, fpos, cachep);
+ return smb_proc_readdir_short(filp, dirent, filldir, ctl);
}
/*
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
index cb1e697fc..6bad6d304 100644
--- a/fs/smbfs/sock.c
+++ b/fs/smbfs/sock.c
@@ -109,7 +109,7 @@ smb_data_callback(void* ptr)
struct data_callback* job=ptr;
struct socket *socket = job->sk->socket;
unsigned char peek_buf[4];
- int result;
+ int result = 0;
mm_segment_t fs;
int count = 100; /* this is a lot, we should have some data waiting */
int found = 0;
@@ -477,14 +477,12 @@ smb_receive_trans2(struct smb_sb_info *server,
unsigned int total_p = 0, total_d = 0, buf_len = 0;
int result;
- while (1)
- {
+ while (1) {
result = smb_receive(server);
if (result < 0)
goto out;
inbuf = server->packet;
- if (server->rcls != 0)
- {
+ if (server->rcls != 0) {
*parm = *data = inbuf;
*ldata = *lparm = 0;
goto out;
@@ -508,13 +506,11 @@ smb_receive_trans2(struct smb_sb_info *server,
parm_len += parm_count;
data_len += data_count;
- if (!rcv_buf)
- {
+ if (!rcv_buf) {
/*
* Check for fast track processing ... just this packet.
*/
- if (parm_count == parm_tot && data_count == data_tot)
- {
+ if (parm_count == parm_tot && data_count == data_tot) {
VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n",
parm_disp, parm_offset, parm_count,
data_disp, data_offset, data_count);
@@ -523,10 +519,6 @@ smb_receive_trans2(struct smb_sb_info *server,
goto success;
}
- if (parm_tot > TRANS2_MAX_TRANSFER ||
- data_tot > TRANS2_MAX_TRANSFER)
- goto out_too_long;
-
/*
* Save the total parameter and data length.
*/
@@ -537,14 +529,15 @@ smb_receive_trans2(struct smb_sb_info *server,
if (server->packet_size > buf_len)
buf_len = server->packet_size;
buf_len = smb_round_length(buf_len);
+ if (buf_len > SMB_MAX_PACKET_SIZE)
+ goto out_too_long;
rcv_buf = smb_vmalloc(buf_len);
if (!rcv_buf)
goto out_no_mem;
*parm = rcv_buf;
*data = rcv_buf + total_p;
- }
- else if (data_tot > total_d || parm_tot > total_p)
+ } else if (data_tot > total_d || parm_tot > total_p)
goto out_data_grew;
if (parm_disp + parm_count > total_p)
@@ -571,8 +564,7 @@ smb_receive_trans2(struct smb_sb_info *server,
* old one, in which case we just copy the data.
*/
inbuf = server->packet;
- if (buf_len >= server->packet_size)
- {
+ if (buf_len >= server->packet_size) {
server->packet_size = buf_len;
server->packet = rcv_buf;
rcv_buf = inbuf;
@@ -716,6 +708,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
struct socket *sock = server_sock(server);
struct scm_cookie scm;
int err;
+ int mparam, mdata;
/* I know the following is very ugly, but I want to build the
smb packet as efficiently as possible. */
@@ -737,19 +730,30 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
struct iovec iov[4];
struct msghdr msg;
- /* N.B. This test isn't valid! packet_size may be < max_xmit */
+ /* FIXME! this test needs to include SMB overhead too, I think ... */
if ((bcc + oparam) > server->opt.max_xmit)
- {
return -ENOMEM;
- }
p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
+ /*
+ * max parameters + max data + max setup == max_xmit to make NT4 happy
+ * and not abort the transfer or split into multiple responses.
+ *
+ * -100 is to make room for headers, which OS/2 seems to include in the
+ * size calculation while NT4 does not?
+ */
+ mparam = SMB_TRANS2_MAX_PARAM;
+ mdata = server->opt.max_xmit - mparam - 100;
+ if (mdata < 1024) {
+ mdata = 1024;
+ mparam = 20;
+ }
+
WSET(server->packet, smb_tpscnt, lparam);
WSET(server->packet, smb_tdscnt, ldata);
- /* N.B. these values should reflect out current packet size */
- WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
- WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
- WSET(server->packet, smb_msrcnt, 0);
+ WSET(server->packet, smb_mprcnt, mparam);
+ WSET(server->packet, smb_mdrcnt, mdata);
+ WSET(server->packet, smb_msrcnt, 0); /* max setup always 0 ? */
WSET(server->packet, smb_flags, 0);
DSET(server->packet, smb_timeout, 0);
WSET(server->packet, smb_pscnt, lparam);
@@ -781,8 +785,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
iov[3].iov_len = ldata;
err = scm_send(sock, &msg, &scm);
- if (err >= 0)
- {
+ if (err >= 0) {
err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
scm_destroy(&scm);
}