summaryrefslogtreecommitdiffstats
path: root/fs/smbfs/dir.c
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/dir.c
parent71118c319fcae4a138f16e35b4f7e0a6d53ce2ca (diff)
Merge with Linux 2.4.2.
Diffstat (limited to 'fs/smbfs/dir.c')
-rw-r--r--fs/smbfs/dir.c197
1 files changed, 142 insertions, 55 deletions
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);