/* * linux/fs/affs/dir.c * * (c) 1996 Hans-Joachim Widmaier - Rewritten * * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. * * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem. * * (C) 1991 Linus Torvalds - minix filesystem * * affs directory handling functions * */ #include #include #include #include #include #include #include #include #include static int affs_readdir(struct inode *, struct file *, void *, filldir_t); static long affs_dir_read(struct inode * inode, struct file * filp, char * buf, unsigned long count); static struct file_operations affs_dir_operations = { NULL, /* lseek - default */ affs_dir_read, /* read */ NULL, /* write - bad */ affs_readdir, /* readdir */ NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ file_fsync /* default fsync */ }; /* * directories can handle most operations... */ struct inode_operations affs_dir_inode_operations = { &affs_dir_operations, /* default directory file-ops */ affs_create, /* create */ affs_lookup, /* lookup */ affs_link, /* link */ affs_unlink, /* unlink */ affs_symlink, /* symlink */ affs_mkdir, /* mkdir */ affs_rmdir, /* rmdir */ NULL, /* mknod */ affs_rename, /* rename */ NULL, /* readlink */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permissions */ }; static long affs_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count) { return -EISDIR; } static int affs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir) { int j, namelen; s32 i; int hash_pos; int chain_pos; unsigned long ino; unsigned long old; int stored; char *name; struct buffer_head *dir_bh; struct buffer_head *fh_bh; struct inode *dir; pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos); if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; stored = 0; dir_bh = NULL; fh_bh = NULL; dir = NULL; old = filp->f_pos & 0x80000000; filp->f_pos &= 0x7FFFFFFF; if (filp->f_pos == 0) { filp->private_data = (void *)0; if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) { return 0; } ++filp->f_pos; stored++; } if (filp->f_pos == 1) { if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) { filp->f_pos |= 0x80000000; return stored; } filp->f_pos = 2; stored++; } /* Read original if this is a link */ ino = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino; if (!(dir = iget(inode->i_sb,ino))) return stored; chain_pos = (filp->f_pos - 2) & 0xffff; hash_pos = (filp->f_pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(inode->i_sb,"readdir","More than 65535 entries in chain"); chain_pos = 0; hash_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } if (!(dir_bh = affs_bread(inode->i_dev,ino,AFFS_I2BSIZE(inode)))) goto readdir_done; while (!stored || !old) { while (hash_pos < AFFS_I2HSIZE(inode) && !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]) hash_pos++; if (hash_pos >= AFFS_I2HSIZE(inode)) goto readdir_done; i = htonl(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]); j = chain_pos; /* If the directory hasn't changed since the last call to readdir(), * we can jump directly to where we left off. */ if (filp->private_data && filp->f_version == dir->i_version) { i = (s32)filp->private_data; j = 0; pr_debug("AFFS: readdir() left off=%d\n",i); } filp->f_version = dir->i_version; pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos); while (i) { if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"readdir","Cannot read block %d",i); goto readdir_done; } ino = i; i = htonl(FILE_END(fh_bh->b_data,inode)->hash_chain); if (j == 0) break; affs_brelse(fh_bh); fh_bh = NULL; j--; } if (fh_bh) { namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name); pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%d\n", namelen,name,ino,i); filp->private_data = (void *)ino; if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0) goto readdir_done; filp->private_data = (void *)i; affs_brelse(fh_bh); fh_bh = NULL; stored++; } if (i == 0) { hash_pos++; chain_pos = 0; } else chain_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } readdir_done: filp->f_pos |= old; affs_brelse(dir_bh); affs_brelse(fh_bh); iput(dir); pr_debug("AFFS: readdir()=%d\n",stored); return stored; }