patch-2.4.19 linux-2.4.19/fs/coda/dir.c
Next file: linux-2.4.19/fs/coda/file.c
Previous file: linux-2.4.19/fs/coda/coda_linux.c
Back to the patch index
Back to the overall index
- Lines: 386
- Date:
Fri Aug 2 17:39:45 2002
- Orig file:
linux-2.4.18/fs/coda/dir.c
- Orig date:
Mon Feb 25 11:38:08 2002
diff -urN linux-2.4.18/fs/coda/dir.c linux-2.4.19/fs/coda/dir.c
@@ -50,11 +50,8 @@
static int coda_dentry_delete(struct dentry *);
/* support routines */
-static void coda_prepare_fakefile(struct file *coda_file,
- struct dentry *open_dentry,
- struct file *open_file);
-static int coda_venus_readdir(struct file *filp, void *dirent,
- filldir_t filldir);
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir);
int coda_fsync(struct file *, struct dentry *dentry, int datasync);
int coda_hasmknod;
@@ -209,7 +206,7 @@
int error=0;
const char *name=de->d_name.name;
int length=de->d_name.len;
- struct inode *inode = NULL;
+ struct inode *inode;
struct ViceFid newfid;
struct coda_vattr attrs;
@@ -247,7 +244,7 @@
int error=0;
const char *name=de->d_name.name;
int length=de->d_name.len;
- struct inode *result = NULL;
+ struct inode *inode;
struct ViceFid newfid;
struct coda_vattr attrs;
@@ -272,23 +269,22 @@
return error;
}
- error = coda_cnode_make(&result, &newfid, dir->i_sb);
- if ( error ) {
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
d_drop(de);
- result = NULL;
- return error;
+ return PTR_ERR(inode);
}
/* invalidate the directory cnode's attributes */
coda_dir_changed(dir, 0);
- d_instantiate(de, result);
+ d_instantiate(de, inode);
return 0;
}
static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
{
struct inode *inode;
- struct coda_vattr attr;
+ struct coda_vattr attrs;
const char *name = de->d_name.name;
int len = de->d_name.len;
int error;
@@ -302,9 +298,9 @@
CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n",
name, len, coda_i2s(dir), mode);
- attr.va_mode = mode;
+ attrs.va_mode = mode;
error = venus_mkdir(dir->i_sb, coda_i2f(dir),
- name, len, &newfid, &attr);
+ name, len, &newfid, &attrs);
if ( error ) {
CDEBUG(D_INODE, "mkdir error: %s result %d\n",
@@ -316,10 +312,10 @@
CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n",
coda_f2s(&newfid));
- error = coda_cnode_make(&inode, &newfid, dir->i_sb);
- if ( error ) {
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
d_drop(de);
- return error;
+ return PTR_ERR(inode);
}
/* invalidate the directory cnode's attributes */
@@ -490,156 +486,143 @@
/* file operations for directories */
-int coda_readdir(struct file *file, void *dirent, filldir_t filldir)
+int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir)
{
- int result = 0;
- struct dentry *cdentry;
- struct inode *cinode, *inode = file->f_dentry->d_inode;
- struct file *cfile, fakefile;
- struct coda_inode_info *cii = ITOC(inode);
+ struct dentry *coda_dentry = coda_file->f_dentry;
+ struct coda_file_info *cfi;
+ struct file *host_file;
+ int ret;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
coda_vfs_stat.readdir++;
- cfile = cii->c_container;
- if (!cfile) BUG();
+ down(&host_file->f_dentry->d_inode->i_sem);
+ host_file->f_pos = coda_file->f_pos;
- cinode = cii->c_container->f_dentry->d_inode;
- if ( S_ISREG(cinode->i_mode) ) {
+ if ( !host_file->f_op->readdir ) {
/* Venus: we must read Venus dirents from the file */
- cdentry = cii->c_container->f_dentry;
- coda_prepare_fakefile(file, cdentry, &fakefile);
-
- result = coda_venus_readdir(&fakefile, dirent, filldir);
+ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry);
+ } else {
+ /* potemkin case: we were handed a directory inode */
+ ret = vfs_readdir(host_file, filldir, dirent);
+ }
- file->f_pos = fakefile.f_pos;
- file->f_version = fakefile.f_version;
- } else {
- /* potemkin case: we are handed a directory inode */
- result = vfs_readdir(file, filldir, dirent);
- }
+ coda_file->f_pos = host_file->f_pos;
+ up(&host_file->f_dentry->d_inode->i_sem);
- UPDATE_ATIME(inode);
- return result;
+ return ret;
}
-/* support routines */
-
-/* instantiate a fake file to pass to coda_venus_readdir */
-static void coda_prepare_fakefile(struct file *coda_file,
- struct dentry *cont_dentry,
- struct file *fake_file)
-{
- fake_file->f_dentry = cont_dentry;
- fake_file->f_pos = coda_file->f_pos;
- fake_file->f_version = coda_file->f_version;
- fake_file->f_op = cont_dentry->d_inode->i_fop;
- fake_file->f_flags = coda_file->f_flags;
- return ;
-}
-
-/*
- * this structure is manipulated by filldir in vfs layer.
- * the count holds the remaining amount of space in the getdents buffer,
- * beyond the current_dir pointer.
- *
- * What structure is this comment referring to?? -JH
- */
+static inline unsigned int CDT2DT(unsigned char cdt)
+{
+ unsigned int dt;
-/* should be big enough to hold any single directory entry */
-#define DIR_BUFSIZE 2048
+ switch(cdt) {
+ case CDT_UNKNOWN: dt = DT_UNKNOWN; break;
+ case CDT_FIFO: dt = DT_FIFO; break;
+ case CDT_CHR: dt = DT_CHR; break;
+ case CDT_DIR: dt = DT_DIR; break;
+ case CDT_BLK: dt = DT_BLK; break;
+ case CDT_REG: dt = DT_REG; break;
+ case CDT_LNK: dt = DT_LNK; break;
+ case CDT_SOCK: dt = DT_SOCK; break;
+ case CDT_WHT: dt = DT_WHT; break;
+ default: dt = DT_UNKNOWN; break;
+ }
+ return dt;
+}
-static int coda_venus_readdir(struct file *filp, void *getdent,
- filldir_t filldir)
+/* support routines */
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir)
{
- int bufsize;
- int offset = filp->f_pos; /* offset in the directory file */
- int count = 0;
- int pos = 0; /* offset in the block we read */
- int result = 0; /* either an error or # of entries returned */
- int errfill;
- char *buff = NULL;
- struct venus_dirent *vdirent;
- int string_offset = (int) (&((struct venus_dirent *)(0))->d_name);
- int i;
-
- CODA_ALLOC(buff, char *, DIR_BUFSIZE);
- if ( !buff ) {
- printk("coda_venus_readdir: out of memory.\n");
- return -ENOMEM;
- }
-
- /* we use this routine to read the file into our buffer */
- bufsize = kernel_read(filp, filp->f_pos, buff, DIR_BUFSIZE);
- if ( bufsize < 0) {
- printk("coda_venus_readdir: cannot read directory %d.\n",
- bufsize);
- result = bufsize;
- goto exit;
- }
- if ( bufsize == 0) {
- result = 0;
- goto exit;
- }
-
- /* Parse and write into user space. Filldir tells us when done! */
- CDEBUG(D_FILE, "buffsize: %d offset %d, count %d.\n",
- bufsize, offset, count);
-
- i = 0;
- result = 0;
- while ( pos + string_offset < bufsize && i < 1024) {
- vdirent = (struct venus_dirent *) (buff + pos);
-
- /* test if the name is fully in the buffer */
- if ( pos + string_offset + (int) vdirent->d_namlen >= bufsize ){
- if ( result == 0 )
- printk("CODA: Invalid directory cfino: %ld\n",
- filp->f_dentry->d_inode->i_ino);
- break;
- }
- /* now we are certain that we can read the entry from buff */
+ int result = 0; /* # of entries returned */
+ 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;
+
+ vdir = (struct venus_dirent *)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;
+ 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;
+ result++;
+ filp->f_pos++;
+ /* fallthrough */
+ default:
+ while (1) {
+ /* read entries from the directory file */
+ ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir,
+ sizeof(*vdir));
+ if (ret < 0) {
+ printk("coda_venus_readdir: read dir failed %d\n", ret);
+ break;
+ }
+ if (ret == 0) break; /* end of directory file reached */
- /* if we don't have a null entry, copy it */
- if ( vdirent->d_fileno && vdirent->d_reclen ) {
- int namlen = vdirent->d_namlen;
- off_t offs = filp->f_pos;
- ino_t ino = vdirent->d_fileno;
- char *name = vdirent->d_name;
-
- errfill = filldir(getdent, name, namlen,
- offs, ino, DT_UNKNOWN);
-CDEBUG(D_FILE, "entry %d: ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %*s, offset %d, result: %d, errfill: %d.\n", i,vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, vdirent->d_namlen, vdirent->d_name, (u_int) offs, result, errfill);
- /* errfill means no space for filling in this round */
- if ( errfill < 0 ) {
- result = 0;
- break;
- }
- /* adjust count */
- result++;
- }
- /* next one */
- filp->f_pos += vdirent->d_reclen;
- if ( filp->f_pos > filp->f_dentry->d_inode->i_size )
- break;
- if ( !vdirent->d_reclen ) {
- printk("CODA: Invalid directory, cfino: %ld\n",
+ /* catch truncated reads */
+ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
+ printk("coda_venus_readdir: short read: %ld\n",
filp->f_dentry->d_inode->i_ino);
- result = -EINVAL;
+ ret = -EBADF;
+ break;
+ }
+ /* validate whether the directory file actually makes sense */
+ if (vdir->d_reclen < vdir_size + vdir->d_namlen ||
+ vdir->d_namlen > CODA_MAXNAMLEN) {
+ printk("coda_venus_readdir: Invalid dir: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ ret = -EBADF;
break;
}
- pos += (unsigned int) vdirent->d_reclen;
- i++;
- }
- if ( i >= 1024 ) {
- printk("Repeating too much in readdir %ld\n",
- filp->f_dentry->d_inode->i_ino);
- result = -EINVAL;
- }
+ name.len = vdir->d_namlen;
+ name.name = vdir->d_name;
-exit:
- CODA_FREE(buff, DIR_BUFSIZE);
- return result;
+ /* Make sure we skip '.' and '..', we already got those */
+ if (name.name[0] == '.' && (name.len == 1 ||
+ (vdir->d_name[1] == '.' && name.len == 2)))
+ vdir->d_fileno = name.len = 0;
+
+ /* skip null entries */
+ if (vdir->d_fileno && name.len) {
+ /* try to look up this entry in the dcache, that way
+ * userspace doesn't have to worry about breaking
+ * getcwd by having mismatched inode numbers for
+ * internal volume mountpoints. */
+ ino = find_inode_number(dir, &name);
+ if (!ino) ino = vdir->d_fileno;
+
+ type = CDT2DT(vdir->d_type);
+ ret = filldir(dirent, name.name, name.len, filp->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;
+ }
+ }
+ kfree(vdir);
+ return result ? result : ret;
}
/* called when a cache lookup succeeds */
@@ -754,9 +737,7 @@
if (inode->i_ino != old_ino)
goto return_bad_inode;
- if ( cii->c_flags )
- coda_flag_inode_children(inode, C_FLUSH);
-
+ coda_flag_inode_children(inode, C_FLUSH);
cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
}
@@ -765,12 +746,6 @@
return 0;
return_bad_inode:
- inode->i_mapping = &inode->i_data;
- if (cii->c_container) {
- fput(cii->c_container);
- cii->c_container = NULL;
- }
- make_bad_inode(inode);
unlock_kernel();
return -EIO;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)