patch-2.1.58 linux/fs/smbfs/dir.c
Next file: linux/fs/smbfs/file.c
Previous file: linux/fs/smbfs/cache.c
Back to the patch index
Back to the overall index
- Lines: 659
- Date:
Sun Oct 12 10:17:05 1997
- Orig file:
v2.1.57/linux/fs/smbfs/dir.c
- Orig date:
Tue Sep 23 16:48:49 1997
diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
@@ -23,21 +23,17 @@
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
-#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino))
-
-static long
-smb_dir_read(struct inode *inode, struct file *filp,
- char *buf, unsigned long count);
-
-static int
-smb_readdir(struct file *filp, void *dirent, filldir_t filldir);
+static long smb_dir_read(struct inode *, struct file *, char *, unsigned long);
+static int smb_readdir(struct file *, void *, filldir_t);
+static int smb_dir_open(struct inode *, struct file *);
static int smb_lookup(struct inode *, struct dentry *);
static int smb_create(struct inode *, struct dentry *, int);
static int smb_mkdir(struct inode *, struct dentry *, int);
static int smb_rmdir(struct inode *, struct dentry *);
static int smb_unlink(struct inode *, struct dentry *);
-static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+static int smb_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
static struct file_operations smb_dir_operations =
{
@@ -48,7 +44,7 @@
NULL, /* poll - default */
smb_ioctl, /* ioctl */
NULL, /* mmap */
- NULL, /* no special open code */
+ smb_dir_open, /* open(struct inode *, struct file *) */
NULL, /* no special release code */
NULL /* fsync */
};
@@ -75,15 +71,6 @@
NULL /* smap */
};
-static void smb_put_dentry(struct dentry *);
-static struct dentry_operations smbfs_dentry_operations =
-{
- NULL, /* revalidate */
- NULL, /* d_hash */
- NULL, /* d_compare */
- smb_put_dentry /* d_delete */
-};
-
static long
smb_dir_read(struct inode *inode, struct file *filp, char *buf,
unsigned long count)
@@ -92,120 +79,43 @@
}
/*
- * This is the callback from dput(). We close the file so that
- * cached dentries don't keep the file open.
+ * Compute the hash for a qstr.
+ * N.B. Move to include/linux/dcache.h?
*/
-void
-smb_put_dentry(struct dentry *dentry)
-{
- struct inode *ino = dentry->d_inode;
- if (ino)
- smb_close(ino);
-}
-
-/* Static variables for the dir cache */
-static struct smb_dirent *c_entry = NULL;
-static struct super_block * c_sb = NULL;
-static unsigned long c_ino = 0;
-static int c_seen_eof;
-static int c_size;
-static int c_last_returned_index;
-
-static struct smb_dirent *
-smb_search_in_cache(struct inode *dir, unsigned long f_pos)
+static unsigned int
+hash_it(const char * name, unsigned int len)
{
- int i;
-
- if (this_dir_cached(dir))
- for (i = 0; i < c_size; i++)
- {
- if (c_entry[i].f_pos < f_pos)
- continue;
- if (c_entry[i].f_pos == f_pos)
- {
- c_last_returned_index = i;
- return &(c_entry[i]);
- }
- break;
- }
- return NULL;
-}
-
-/*
- * Compute the hash for a qstr ... move to include/linux/dcache.h?
- */
-static unsigned int hash_it(const char * name, unsigned int len)
-{
- unsigned long hash;
- hash = init_name_hash();
+ unsigned long hash = init_name_hash();
while (len--)
hash = partial_name_hash(*name++, hash);
return end_name_hash(hash);
}
-static struct semaphore refill_cache_sem = MUTEX;
/*
- * Called with the refill semaphore held.
+ * If a dentry already exists, we have to give the cache entry
+ * the correct inode number. This is needed for getcwd().
*/
-static int
-smb_refill_dir_cache(struct dentry *dentry, unsigned long f_pos)
+static unsigned long
+smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
{
- struct inode *dir = dentry->d_inode;
- ino_t ino_start;
- int i, result;
-
- result = smb_proc_readdir(dentry, f_pos,
- SMB_READDIR_CACHE_SIZE, c_entry);
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_refill_dir_cache: dir=%s/%s, pos=%lu, result=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, f_pos, result);
-#endif
-
- if (result <= 0)
+ struct dentry * new_dentry;
+ struct qstr qname;
+ unsigned long ino = 0;
+
+ qname.name = entry->name;
+ qname.len = entry->len;
+ qname.hash = hash_it(qname.name, qname.len);
+ new_dentry = d_lookup(dentry, &qname);
+ if (new_dentry)
{
- /*
- * If an error occurred, the cache may have been partially
- * filled prior to failing, so we must invalidate.
- * N.B. Might not need to for 0 return ... save cache?
- */
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
- goto out;
- }
-
- /* Suppose there are a multiple of cache entries? */
- c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
- c_sb = dir->i_sb;
- c_ino = dir->i_ino;
- c_size = result;
- c_last_returned_index = 0; /* is this used? */
-
- ino_start = smb_invent_inos(c_size);
- /*
- * If a dentry already exists, we have to give the cache entry
- * the correct inode number. This is needed for getcwd().
- */
- for (i = 0; i < c_size; i++)
- {
- struct dentry * new_dentry;
- struct qstr qname;
-
- c_entry[i].attr.f_ino = ino_start++;
- qname.name = c_entry[i].name;
- qname.len = c_entry[i].len;
- qname.hash = hash_it(qname.name, qname.len);
- new_dentry = d_lookup(dentry, &qname);
- if (new_dentry)
- {
- struct inode * inode = new_dentry->d_inode;
- if (inode)
- c_entry[i].attr.f_ino = inode->i_ino;
- dput(new_dentry);
- }
+ struct inode * inode = new_dentry->d_inode;
+ if (inode)
+ ino = inode->i_ino;
+ dput(new_dentry);
}
-out:
- return result;
+ if (!ino)
+ ino = smb_invent_inos(1);
+ return ino;
}
static int
@@ -213,150 +123,195 @@
{
struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_inode;
- struct smb_dirent *entry;
+ struct cache_head *cachep;
int result;
pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
dir->i_ino, c_ino);
-
- result = -EBADF;
- if ((dir == NULL) || !S_ISDIR(dir->i_mode))
+ /*
+ * Make sure our inode is up-to-date.
+ */
+ result = smb_revalidate_inode(dir);
+ if (result)
+ goto out;
+ /*
+ * Get the cache pointer ...
+ */
+ cachep = smb_get_dircache(dentry);
+ if (!cachep)
goto out;
-
/*
- * Check whether the directory cache exists yet
+ * Make sure the cache is up-to-date.
*/
- if (c_entry == NULL)
+ if (!cachep->valid)
{
- int size = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
- result = -ENOMEM;
- entry = (struct smb_dirent *) smb_vmalloc(size);
- /*
- * Somebody else may have allocated the cache,
- * so we check again to avoid a memory leak.
- */
- if (!c_entry)
- {
- if (!entry)
- goto out;
- c_entry = entry;
- } else if (entry) {
- printk("smb_readdir: cache already alloced!\n");
- smb_vfree(entry);
- }
+ result = smb_refill_dircache(cachep, dentry);
+ if (result)
+ goto up_and_out;
}
- result = 0;
switch ((unsigned int) filp->f_pos)
{
case 0:
if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 1;
case 1:
if (filldir(dirent, "..", 2, 1,
dentry->d_parent->d_inode->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 2;
}
- /*
- * Since filldir() could block if dirent is paged out,
- * we hold the refill semaphore while using the cache.
- * N.B. It's possible that the directory could change
- * between calls to readdir ... what to do??
- */
- down(&refill_cache_sem);
- entry = smb_search_in_cache(dir, filp->f_pos);
- if (entry == NULL)
- {
- /* Past the end of _this_ directory? */
- if (this_dir_cached(dir) && c_seen_eof &&
- filp->f_pos == c_entry[c_size-1].f_pos + 1)
+ while (1)
+ {
+ struct cache_dirent this_dirent, *entry = &this_dirent;
+
+ if (!smb_find_in_cache(cachep, filp->f_pos, entry))
+ break;
+ /*
+ * Check whether to look up the inode number.
+ */
+ if (!entry->ino)
{
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_readdir: eof reached for %s/%s, c_size=%d, pos=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, c_size, (int) filp->f_pos);
-#endif
- goto up_and_out;
+ entry->ino = smb_find_ino(dentry, entry);
}
- result = smb_refill_dir_cache(dentry, filp->f_pos);
- if (result <= 0)
- goto up_and_out;
- entry = c_entry;
- }
-
- while (entry < &(c_entry[c_size]))
- {
- pr_debug("smb_readdir: entry->name = %s\n", entry->name);
if (filldir(dirent, entry->name, entry->len,
- entry->f_pos, entry->attr.f_ino) < 0)
+ filp->f_pos, entry->ino) < 0)
break;
-#if SMBFS_PARANOIA
-/* should never happen */
-if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos))
-printk("smb_readdir: cache changed!\n");
-#endif
filp->f_pos += 1;
- entry += 1;
}
result = 0;
+ /*
+ * Release the dircache.
+ */
up_and_out:
- up(&refill_cache_sem);
+ smb_free_dircache(cachep);
out:
return result;
}
-void
-smb_init_dir_cache(void)
+static int
+smb_dir_open(struct inode *dir, struct file *file)
{
- c_entry = NULL;
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_dir_open: (%s/%s)\n", file->f_dentry->d_parent->d_name.name,
+file->f_dentry->d_name.name);
+#endif
+ return smb_revalidate_inode(dir);
}
-void
-smb_invalid_dir_cache(struct inode * dir)
+/*
+ * Dentry operations routines
+ */
+static int smb_lookup_validate(struct dentry *);
+static void smb_delete_dentry(struct dentry *);
+
+static struct dentry_operations smbfs_dentry_operations =
+{
+ smb_lookup_validate, /* d_validate(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ smb_delete_dentry /* d_delete(struct dentry *) */
+};
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+static int smb_lookup_validate(struct dentry * dentry)
{
- if (this_dir_cached(dir))
+ 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
+ * successful server lookup renews the timestamp.)
+ */
+ valid = age < 5 * HZ || IS_ROOT(dentry);
+#ifdef SMBFS_DEBUG_VERBOSE
+if (!valid)
+printk("smb_lookup_validate: %s/%s not valid, age=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, age)
+#endif
+
+ if (inode)
{
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
+ if (is_bad_inode(inode))
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_lookup_validate: %s/%s has dud inode\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ valid = 0;
+ }
+ } else
+ {
+ /*
+ * What should we do for negative dentries?
+ */
}
+ return valid;
}
-void
-smb_free_dir_cache(void)
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to close files and unhash dentries with bad inodes.
+ */
+static void smb_delete_dentry(struct dentry * dentry)
{
- if (c_entry != NULL)
+ if (dentry->d_inode)
+ {
+ if (is_bad_inode(dentry->d_inode))
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_delete_dentry: bad inode, unhashing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ d_drop(dentry);
+ }
+ smb_close_dentry(dentry);
+ } else
{
- /* N.B. can this block?? */
- smb_vfree(c_entry);
+ /* N.B. Unhash negative dentries? */
+ }
+}
+
+/*
+ * 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?
+ */
+void smb_renew_times(struct dentry * dentry)
+{
+ for (;;) {
+ dentry->d_time = jiffies;
+ if (dentry == dentry->d_parent)
+ break;
+ dentry = dentry->d_parent;
}
- c_entry = NULL;
}
static int
-smb_lookup(struct inode *dir, struct dentry *d_entry)
+smb_lookup(struct inode *dir, struct dentry *dentry)
{
struct smb_fattr finfo;
struct inode *inode;
int error;
error = -ENAMETOOLONG;
- if (d_entry->d_name.len > SMB_MAXNAMELEN)
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
- error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo);
-#if SMBFS_PARANOIA
+ error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &finfo);
+#ifdef SMBFS_PARANOIA
if (error && error != -ENOENT)
printk("smb_lookup: find %s/%s failed, error=%d\n",
-d_entry->d_parent->d_name.name, d_entry->d_name.name, error);
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
#endif
inode = NULL;
@@ -370,10 +325,11 @@
if (inode)
{
/* cache the dentry pointer */
- inode->u.smbfs_i.dentry = d_entry;
+ inode->u.smbfs_i.dentry = dentry;
add_entry:
- d_entry->d_op = &smbfs_dentry_operations;
- d_add(d_entry, inode);
+ dentry->d_op = &smbfs_dentry_operations;
+ d_add(dentry, inode);
+ smb_renew_times(dentry);
error = 0;
}
}
@@ -385,25 +341,24 @@
* This code is common to all routines creating a new inode.
*/
static int
-smb_instantiate(struct inode *dir, struct dentry *dentry)
+smb_instantiate(struct dentry *dentry)
{
struct smb_fattr fattr;
int error;
- smb_invalid_dir_cache(dir);
-
error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
if (!error)
{
struct inode *inode;
error = -EACCES;
fattr.f_ino = smb_invent_inos(1);
- inode = smb_iget(dir->i_sb, &fattr);
+ inode = smb_iget(dentry->d_sb, &fattr);
if (inode)
{
/* cache the dentry pointer */
inode->u.smbfs_i.dentry = dentry;
d_instantiate(dentry, inode);
+ smb_renew_times(dentry);
error = 0;
}
}
@@ -424,11 +379,12 @@
* state. Currently we close it directly again, although this
* is not necessary anymore. */
+ smb_invalid_dir_cache(dir);
error = smb_proc_create(dentry->d_parent, &(dentry->d_name),
0, CURRENT_TIME);
if (!error)
{
- error = smb_instantiate(dir, dentry);
+ error = smb_instantiate(dentry);
}
out:
return error;
@@ -444,10 +400,11 @@
if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
+ smb_invalid_dir_cache(dir);
error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
- error = smb_instantiate(dir, dentry);
+ error = smb_instantiate(dentry);
}
out:
return error;
@@ -459,7 +416,7 @@
int error;
error = -ENAMETOOLONG;
- if (dentry->d_name.len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
/*
@@ -468,11 +425,12 @@
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);
+ smb_invalid_dir_cache(dir);
error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
+ smb_renew_times(dentry);
d_delete(dentry);
}
out:
@@ -494,11 +452,12 @@
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);
+ smb_invalid_dir_cache(dir);
error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name));
if (!error)
{
+ smb_renew_times(dentry);
d_delete(dentry);
}
out:
@@ -511,19 +470,6 @@
{
int error;
- error = -ENOTDIR;
- if (!old_dir || !S_ISDIR(old_dir->i_mode))
- {
- printk("smb_rename: old inode is NULL or not a directory\n");
- goto out;
- }
-
- if (!new_dir || !S_ISDIR(new_dir->i_mode))
- {
- printk("smb_rename: new inode is NULL or not a directory\n");
- goto out;
- }
-
error = -ENAMETOOLONG;
if (old_dentry->d_name.len > SMB_MAXNAMELEN ||
new_dentry->d_name.len > SMB_MAXNAMELEN)
@@ -538,10 +484,8 @@
if (new_dentry->d_inode)
smb_close(new_dentry->d_inode);
- /* Assume success and invalidate now */
smb_invalid_dir_cache(old_dir);
smb_invalid_dir_cache(new_dir);
-
error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
new_dentry->d_parent, &(new_dentry->d_name));
/*
@@ -549,22 +493,24 @@
*/
if (error == -EEXIST)
{
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: existing file %s/%s, d_count=%d\n",
new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
new_dentry->d_count);
#endif
error = smb_proc_unlink(new_dentry->d_parent,
&(new_dentry->d_name));
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: after unlink error=%d\n", error);
#endif
- if (error)
- goto out;
- d_delete(new_dentry);
-
- error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
- new_dentry->d_parent, &(new_dentry->d_name));
+ if (!error)
+ {
+ d_delete(new_dentry);
+ error = smb_proc_mv(old_dentry->d_parent,
+ &(old_dentry->d_name),
+ new_dentry->d_parent,
+ &(new_dentry->d_name));
+ }
}
/*
@@ -572,6 +518,8 @@
*/
if (!error)
{
+ smb_renew_times(old_dentry);
+ smb_renew_times(new_dentry->d_parent);
d_move(old_dentry, new_dentry);
}
out:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov