summaryrefslogtreecommitdiffstats
path: root/fs/coda/dir.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--fs/coda/dir.c305
1 files changed, 149 insertions, 156 deletions
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 9ddf5ed62162..f89ff083079b 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -25,7 +25,6 @@
#include <linux/coda_psdev.h>
#include <linux/coda_fs_i.h>
#include <linux/coda_cache.h>
-#include <linux/coda_proc.h>
#include "coda_int.h"
@@ -43,15 +42,15 @@ static int coda_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry);
/* dir file-ops */
-static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
+static int coda_readdir(struct file *file, void *buf, filldir_t filldir);
/* dentry ops */
static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd);
static int coda_dentry_delete(struct dentry *);
/* support routines */
-static int coda_venus_readdir(struct file *filp, filldir_t filldir,
- void *dirent, struct dentry *dir);
+static int coda_venus_readdir(struct file *coda_file, void *buf,
+ filldir_t filldir);
/* same as fs/bad_inode.c */
static int coda_return_EIO(void)
@@ -87,7 +86,6 @@ const struct file_operations coda_dir_operations = {
.read = generic_read_dir,
.readdir = coda_readdir,
.open = coda_open,
- .flush = coda_flush,
.release = coda_release,
.fsync = coda_fsync,
};
@@ -97,58 +95,45 @@ const struct file_operations coda_dir_operations = {
/* access routines: lookup, readlink, permission */
static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd)
{
- struct inode *res_inode = NULL;
+ struct inode *inode = NULL;
struct CodaFid resfid = { { 0, } };
- int dropme = 0; /* to indicate entry should not be cached */
int type = 0;
int error = 0;
const char *name = entry->d_name.name;
size_t length = entry->d_name.len;
-
- if ( length > CODA_MAXNAMLEN ) {
- printk("name too long: lookup, %s (%*s)\n",
+
+ if (length > CODA_MAXNAMLEN) {
+ printk(KERN_ERR "name too long: lookup, %s (%*s)\n",
coda_i2s(dir), (int)length, name);
return ERR_PTR(-ENAMETOOLONG);
}
+ /* control object, create inode on the fly */
+ if (coda_isroot(dir) && coda_iscontrol(name, length)) {
+ error = coda_cnode_makectl(&inode, dir->i_sb);
+ type = CODA_NOCACHE;
+ goto exit;
+ }
+
lock_kernel();
- /* control object, create inode on the fly */
- if (coda_isroot(dir) && coda_iscontrol(name, length)) {
- error = coda_cnode_makectl(&res_inode, dir->i_sb);
- dropme = 1;
- goto exit;
- }
- error = venus_lookup(dir->i_sb, coda_i2f(dir),
- (const char *)name, length, &type, &resfid);
+ error = venus_lookup(dir->i_sb, coda_i2f(dir), name, length,
+ &type, &resfid);
+ if (!error)
+ error = coda_cnode_make(&inode, &resfid, dir->i_sb);
- res_inode = NULL;
- if (!error) {
- if (type & CODA_NOCACHE) {
- type &= (~CODA_NOCACHE);
- dropme = 1;
- }
+ unlock_kernel();
- error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
- if (error) {
- unlock_kernel();
- return ERR_PTR(error);
- }
- } else if (error != -ENOENT) {
- unlock_kernel();
+ if (error && error != -ENOENT)
return ERR_PTR(error);
- }
exit:
- entry->d_time = 0;
entry->d_op = &coda_dentry_operations;
- d_add(entry, res_inode);
- if ( dropme ) {
- d_drop(entry);
- coda_flag_inode(res_inode, C_VATTR);
- }
- unlock_kernel();
- return NULL;
+
+ if (inode && (type & CODA_NOCACHE))
+ coda_flag_inode(inode, C_VATTR | C_PURGE);
+
+ return d_splice_alias(inode, entry);
}
@@ -161,8 +146,6 @@ int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
lock_kernel();
- coda_vfs_stat.permission++;
-
if (coda_cache_check(inode, mask))
goto out;
@@ -173,12 +156,11 @@ int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
out:
unlock_kernel();
-
- return error;
+ return error;
}
-static inline void coda_dir_changed(struct inode *dir, int link)
+static inline void coda_dir_update_mtime(struct inode *dir)
{
#ifdef REQUERY_VENUS_FOR_MTIME
/* invalidate the directory cnode's attributes so we refetch the
@@ -186,12 +168,27 @@ static inline void coda_dir_changed(struct inode *dir, int link)
coda_flag_inode(dir, C_VATTR);
#else
/* optimistically we can also act as if our nose bleeds. The
- * granularity of the mtime is coarse anyways so we might actually be
- * right most of the time. Note: we only do this for directories. */
+ * granularity of the mtime is coarse anyways so we might actually be
+ * right most of the time. Note: we only do this for directories. */
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
#endif
- if (link)
- dir->i_nlink += link;
+}
+
+/* we have to wrap inc_nlink/drop_nlink because sometimes userspace uses a
+ * trick to fool GNU find's optimizations. If we can't be sure of the link
+ * (because of volume mount points) we set i_nlink to 1 which forces find
+ * to consider every child as a possible directory. We should also never
+ * see an increment or decrement for deleted directories where i_nlink == 0 */
+static inline void coda_dir_inc_nlink(struct inode *dir)
+{
+ if (dir->i_nlink >= 2)
+ inc_nlink(dir);
+}
+
+static inline void coda_dir_drop_nlink(struct inode *dir)
+{
+ if (dir->i_nlink > 2)
+ drop_nlink(dir);
}
/* creation routines: create, mknod, mkdir, link, symlink */
@@ -205,7 +202,6 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode, struct na
struct coda_vattr attrs;
lock_kernel();
- coda_vfs_stat.create++;
if (coda_isroot(dir) && coda_iscontrol(name, length)) {
unlock_kernel();
@@ -229,10 +225,10 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode, struct na
}
/* invalidate the directory cnode's attributes */
- coda_dir_changed(dir, 0);
+ coda_dir_update_mtime(dir);
unlock_kernel();
d_instantiate(de, inode);
- return 0;
+ return 0;
}
static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
@@ -245,7 +241,6 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
struct CodaFid newfid;
lock_kernel();
- coda_vfs_stat.mkdir++;
if (coda_isroot(dir) && coda_iscontrol(name, len)) {
unlock_kernel();
@@ -268,12 +263,13 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
d_drop(de);
return PTR_ERR(inode);
}
-
+
/* invalidate the directory cnode's attributes */
- coda_dir_changed(dir, 1);
+ coda_dir_inc_nlink(dir);
+ coda_dir_update_mtime(dir);
unlock_kernel();
d_instantiate(de, inode);
- return 0;
+ return 0;
}
/* try to make de an entry in dir_inodde linked to source_de */
@@ -286,7 +282,6 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode,
int error;
lock_kernel();
- coda_vfs_stat.link++;
if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) {
unlock_kernel();
@@ -296,16 +291,16 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode,
error = venus_link(dir_inode->i_sb, coda_i2f(inode),
coda_i2f(dir_inode), (const char *)name, len);
- if (error) {
+ if (error) {
d_drop(de);
goto out;
}
- coda_dir_changed(dir_inode, 0);
+ coda_dir_update_mtime(dir_inode);
atomic_inc(&inode->i_count);
d_instantiate(de, inode);
inc_nlink(inode);
-
+
out:
unlock_kernel();
return(error);
@@ -318,10 +313,9 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de,
const char *name = de->d_name.name;
int len = de->d_name.len;
int symlen;
- int error=0;
-
+ int error = 0;
+
lock_kernel();
- coda_vfs_stat.symlink++;
if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) {
unlock_kernel();
@@ -336,18 +330,18 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de,
/*
* This entry is now negative. Since we do not create
- * an inode for the entry we have to drop it.
+ * an inode for the entry we have to drop it.
*/
d_drop(de);
- error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
+ error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
symname, symlen);
/* mtime is no good anymore */
if ( !error )
- coda_dir_changed(dir_inode, 0);
+ coda_dir_update_mtime(dir_inode);
unlock_kernel();
- return error;
+ return error;
}
/* destruction routines: unlink, rmdir */
@@ -358,79 +352,70 @@ int coda_unlink(struct inode *dir, struct dentry *de)
int len = de->d_name.len;
lock_kernel();
- coda_vfs_stat.unlink++;
- error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
- if ( error ) {
+ error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
+ if ( error ) {
unlock_kernel();
- return error;
- }
+ return error;
+ }
- coda_dir_changed(dir, 0);
+ coda_dir_update_mtime(dir);
drop_nlink(de->d_inode);
unlock_kernel();
-
- return 0;
+ return 0;
}
int coda_rmdir(struct inode *dir, struct dentry *de)
{
const char *name = de->d_name.name;
int len = de->d_name.len;
- int error;
+ int error;
lock_kernel();
- coda_vfs_stat.rmdir++;
- if (!d_unhashed(de)) {
- unlock_kernel();
- return -EBUSY;
- }
error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
+ if (!error) {
+ /* VFS may delete the child */
+ if (de->d_inode)
+ de->d_inode->i_nlink = 0;
- if ( error ) {
- unlock_kernel();
- return error;
- }
-
- coda_dir_changed(dir, -1);
- drop_nlink(de->d_inode);
- d_delete(de);
+ /* fix the link count of the parent */
+ coda_dir_drop_nlink(dir);
+ coda_dir_update_mtime(dir);
+ }
unlock_kernel();
-
- return 0;
+ return error;
}
/* rename */
-static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
- const char *old_name = old_dentry->d_name.name;
- const char *new_name = new_dentry->d_name.name;
+ const char *old_name = old_dentry->d_name.name;
+ const char *new_name = new_dentry->d_name.name;
int old_length = old_dentry->d_name.len;
int new_length = new_dentry->d_name.len;
- int link_adjust = 0;
- int error;
+ int error;
lock_kernel();
- coda_vfs_stat.rename++;
- error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
- coda_i2f(new_dir), old_length, new_length,
+ error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
+ coda_i2f(new_dir), old_length, new_length,
(const char *) old_name, (const char *)new_name);
- if ( !error ) {
+ if ( !error ) {
if ( new_dentry->d_inode ) {
- if ( S_ISDIR(new_dentry->d_inode->i_mode) )
- link_adjust = 1;
-
- coda_dir_changed(old_dir, -link_adjust);
- coda_dir_changed(new_dir, link_adjust);
+ if ( S_ISDIR(new_dentry->d_inode->i_mode) ) {
+ coda_dir_drop_nlink(old_dir);
+ coda_dir_inc_nlink(new_dir);
+ }
+ coda_dir_update_mtime(old_dir);
+ coda_dir_update_mtime(new_dir);
coda_flag_inode(new_dentry->d_inode, C_VATTR);
} else {
coda_flag_inode(old_dir, C_VATTR);
coda_flag_inode(new_dir, C_VATTR);
- }
+ }
}
unlock_kernel();
@@ -439,44 +424,41 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
/* file operations for directories */
-int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir)
+int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir)
{
- struct dentry *coda_dentry = coda_file->f_path.dentry;
struct coda_file_info *cfi;
struct file *host_file;
- struct inode *host_inode;
int ret;
cfi = CODA_FTOC(coda_file);
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container;
- coda_vfs_stat.readdir++;
-
- host_inode = host_file->f_path.dentry->d_inode;
- mutex_lock(&host_inode->i_mutex);
- host_file->f_pos = coda_file->f_pos;
-
- if (!host_file->f_op->readdir) {
- /* Venus: we must read Venus dirents from the file */
- ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry);
- } else {
- /* potemkin case: we were handed a directory inode. */
- /* Yuk, we can't call vfs_readdir because we are already
- * holding the inode semaphore. */
- ret = -ENOTDIR;
- if (!host_file->f_op || !host_file->f_op->readdir)
- goto out;
+ if (!host_file->f_op)
+ return -ENOTDIR;
+
+ if (host_file->f_op->readdir)
+ {
+ /* potemkin case: we were handed a directory inode.
+ * We can't use vfs_readdir because we have to keep the file
+ * position in sync between the coda_file and the host_file.
+ * and as such we need grab the inode mutex. */
+ struct inode *host_inode = host_file->f_path.dentry->d_inode;
+
+ mutex_lock(&host_inode->i_mutex);
+ host_file->f_pos = coda_file->f_pos;
ret = -ENOENT;
if (!IS_DEADDIR(host_inode)) {
- ret = host_file->f_op->readdir(host_file, filldir, dirent);
+ ret = host_file->f_op->readdir(host_file, buf, filldir);
file_accessed(host_file);
}
+
+ coda_file->f_pos = host_file->f_pos;
+ mutex_unlock(&host_inode->i_mutex);
}
-out:
- coda_file->f_pos = host_file->f_pos;
- mutex_unlock(&host_inode->i_mutex);
+ else /* Venus: we must read Venus dirents from a file */
+ ret = coda_venus_readdir(coda_file, buf, filldir);
return ret;
}
@@ -501,57 +483,68 @@ static inline unsigned int CDT2DT(unsigned char cdt)
}
/* support routines */
-static int coda_venus_readdir(struct file *filp, filldir_t filldir,
- void *dirent, struct dentry *dir)
+static int coda_venus_readdir(struct file *coda_file, void *buf,
+ filldir_t filldir)
{
int result = 0; /* # of entries returned */
+ struct coda_file_info *cfi;
+ struct coda_inode_info *cii;
+ struct file *host_file;
+ struct dentry *de;
struct venus_dirent *vdir;
unsigned long vdir_size =
(unsigned long)(&((struct venus_dirent *)0)->d_name);
unsigned int type;
struct qstr name;
ino_t ino;
- int ret, i;
+ int ret;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ de = coda_file->f_path.dentry;
+ cii = ITOC(de->d_inode);
vdir = kmalloc(sizeof(*vdir), GFP_KERNEL);
if (!vdir) return -ENOMEM;
- i = filp->f_pos;
- switch(i) {
- case 0:
- ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR);
- if (ret < 0) break;
+ if (coda_file->f_pos == 0) {
+ ret = filldir(buf, ".", 1, 0, de->d_inode->i_ino, DT_DIR);
+ if (ret < 0)
+ goto out;
result++;
- filp->f_pos++;
- /* fallthrough */
- case 1:
- ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR);
- if (ret < 0) break;
+ coda_file->f_pos++;
+ }
+ if (coda_file->f_pos == 1) {
+ ret = filldir(buf, "..", 2, 1, de->d_parent->d_inode->i_ino, DT_DIR);
+ if (ret < 0)
+ goto out;
result++;
- filp->f_pos++;
- /* fallthrough */
- default:
+ coda_file->f_pos++;
+ }
while (1) {
/* read entries from the directory file */
- ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir,
+ ret = kernel_read(host_file, coda_file->f_pos - 2, (char *)vdir,
sizeof(*vdir));
if (ret < 0) {
- printk("coda_venus_readdir: read dir failed %d\n", ret);
+ printk(KERN_ERR "coda readdir: read dir %s failed %d\n",
+ coda_f2s(&cii->c_fid), ret);
break;
}
if (ret == 0) break; /* end of directory file reached */
/* catch truncated reads */
if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
- printk("coda_venus_readdir: short read: %ld\n",
- filp->f_path.dentry->d_inode->i_ino);
+ printk(KERN_ERR "coda readdir: short read on %s\n",
+ coda_f2s(&cii->c_fid));
ret = -EBADF;
break;
}
/* validate whether the directory file actually makes sense */
if (vdir->d_reclen < vdir_size + vdir->d_namlen) {
- printk("coda_venus_readdir: Invalid dir: %ld\n",
- filp->f_path.dentry->d_inode->i_ino);
+ printk(KERN_ERR "coda readdir: invalid dir %s\n",
+ coda_f2s(&cii->c_fid));
ret = -EBADF;
break;
}
@@ -570,21 +563,21 @@ static int coda_venus_readdir(struct file *filp, filldir_t filldir,
* userspace doesn't have to worry about breaking
* getcwd by having mismatched inode numbers for
* internal volume mountpoints. */
- ino = find_inode_number(dir, &name);
+ ino = find_inode_number(de, &name);
if (!ino) ino = vdir->d_fileno;
type = CDT2DT(vdir->d_type);
- ret = filldir(dirent, name.name, name.len, filp->f_pos,
- ino, type);
+ ret = filldir(buf, name.name, name.len,
+ coda_file->f_pos, ino, type);
/* failure means no space for filling in this round */
if (ret < 0) break;
result++;
}
/* we'll always have progress because d_reclen is unsigned and
* we've already established it is non-zero. */
- filp->f_pos += vdir->d_reclen;
+ coda_file->f_pos += vdir->d_reclen;
}
- }
+out:
kfree(vdir);
return result ? result : ret;
}