diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-03-09 20:33:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-03-09 20:33:35 +0000 |
commit | 116674acc97ba75a720329996877077d988443a2 (patch) | |
tree | 6a3f2ff0b612ae2ee8a3f3509370c9e6333a53b3 /fs/smbfs | |
parent | 71118c319fcae4a138f16e35b4f7e0a6d53ce2ca (diff) |
Merge with Linux 2.4.2.
Diffstat (limited to 'fs/smbfs')
-rw-r--r-- | fs/smbfs/ChangeLog | 11 | ||||
-rw-r--r-- | fs/smbfs/Makefile | 1 | ||||
-rw-r--r-- | fs/smbfs/cache.c | 408 | ||||
-rw-r--r-- | fs/smbfs/dir.c | 197 | ||||
-rw-r--r-- | fs/smbfs/file.c | 13 | ||||
-rw-r--r-- | fs/smbfs/inode.c | 30 | ||||
-rw-r--r-- | fs/smbfs/proc.c | 386 | ||||
-rw-r--r-- | fs/smbfs/sock.c | 55 |
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); } |