static int ufs_readlink(struct inode * inode, char * buffer, int buflen) { unsigned long int block; struct buffer_head * bh = NULL; char * link; int i; char c; if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) { printk("ufs_readlink: called on ino %lu dev %u/%u\n", inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); } if (!S_ISLNK(inode->i_mode)) { iput (inode); return -EINVAL; } if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; if (inode->i_blocks) { /* XXX - error checking */ block = ufs_bmap(inode, 0); if (inode->i_sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) { printk("ufs_readlink: bmap got %lu for ino %lu\n", block, inode->i_ino); } bh = bread(inode->i_dev, block, BLOCK_SIZE); if (!bh) { iput (inode); printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n", inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); return 0; } link = bh->b_data; } else { link = (char *)&(inode->u.ufs_i.ui_db[0]); } i = 0; while (i < buflen && (c = link[i])) { i++; put_user (c, buffer++); } iput (inode); if (bh) brelse (bh); return i; }
/* * XXX - blatantly stolen from ext2fs */ static int ufs_follow_link(struct inode * dir, struct inode * inode, int flag, int mode, struct inode ** res_inode) { unsigned long int block; int error; struct buffer_head * bh; char * link; bh = NULL; if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) { printk("ufs_follow_link: called on ino %lu dev %u/%u\n", dir->i_ino, MAJOR(dir->i_dev), MINOR(dir->i_dev)); } *res_inode = NULL; if (!dir) { dir = current->fs->root; dir->i_count++; } if (!inode) { iput (dir); return -ENOENT; } if (!S_ISLNK(inode->i_mode)) { iput (dir); *res_inode = inode; return 0; } if (current->link_count > 5) { iput (dir); iput (inode); return -ELOOP; } if (inode->i_blocks) { /* read the link from disk */ /* XXX - error checking */ block = ufs_bmap(inode, 0); bh = bread(inode->i_dev, block, BLOCK_SIZE); if (bh == NULL) { printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n", inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); iput(dir); iput(inode); return(-EIO); } link = bh->b_data; } else { /* fast symlink */ link = (char *)&(inode->u.ufs_i.ui_db[0]); } current->link_count++; error = open_namei (link, flag, mode, res_inode, dir); current->link_count--; iput (inode); if (bh) { brelse (bh); } return(error); }
/* XXX - this is a mess, especially for endianity */ int ufs_lookup (struct inode * dir, const char * name, int len, struct inode ** result) { unsigned long int lfragno, fragno; struct buffer_head * bh; struct ufs_direct * d; /* * Touching /xyzzy in a filesystem toggles debugging messages. */ if ((len == 5) && !(memcmp(name, "xyzzy", len)) && (dir->i_ino == UFS_ROOTINO)) { dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG; printk("UFS debugging %s\n", (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) ? "on": "off"); return(-ENOENT); } /* * Touching /xyzzy.i in a filesystem toggles debugging for ufs_inode.c. */ if ((len == 7) && !(memcmp(name, "xyzzy.i", len)) && (dir->i_ino == UFS_ROOTINO)) { dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_INODE; printk("UFS inode debugging %s\n", (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_INODE) ? "on": "off"); return(-ENOENT); } if ((len == 7) && !(memcmp(name, "xyzzy.n", len)) && (dir->i_ino == UFS_ROOTINO)) { dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_NAMEI; printk("UFS namei debugging %s\n", (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_NAMEI) ? "on": "off"); return(-ENOENT); } if ((len == 7) && !(memcmp(name, "xyzzy.l", len)) && (dir->i_ino == UFS_ROOTINO)) { dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_LINKS; printk("UFS symlink debugging %s\n", (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_LINKS) ? "on": "off"); return(-ENOENT); } if (dir->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_NAMEI)) { printk("ufs_lookup: called for ino %lu name %s\n", dir->i_ino, name); } /* XXX - do I want i_blocks in 512-blocks or 1024-blocks? */ for (lfragno = 0; lfragno < (dir->i_blocks)>>1; lfragno++) { fragno = ufs_bmap(dir, lfragno); /* XXX - ufs_bmap() call needs error checking */ /* XXX - s_blocksize is actually the UFS frag size */ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("ufs_lookup: ino %lu lfragno %lu fragno %lu\n", dir->i_ino, lfragno, fragno); } if (fragno == 0) { /* XXX - bug bug bug */ return(-ENOENT); } bh = bread(dir->i_dev, fragno, dir->i_sb->s_blocksize); if (bh == NULL) { printk("ufs_lookup: bread failed: ino %lu, lfragno %lu", dir->i_ino, lfragno); return(-EIO); } d = (struct ufs_direct *)(bh->b_data); while (((char *)d - bh->b_data + d->d_reclen) <= dir->i_sb->s_blocksize) { /* XXX - skip block if d_reclen or d_namlen is 0 */ if ((d->d_reclen == 0) || (d->d_namlen == 0)) { if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("ufs_lookup: skipped space in directory, ino %lu\n", dir->i_ino); } break; } if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("lfragno 0x%lx direct d 0x%x d_ino %u d_reclen %u d_namlen %u d_name `%s'\n", lfragno, (unsigned int)d, d->d_ino, d->d_reclen, d->d_namlen, d->d_name); } if ((d->d_namlen == len) && /* XXX - don't use strncmp() - see ext2fs */ (ufs_match(len, name, d))) { /* We have a match */ *result = iget(dir->i_sb, d->d_ino); brelse(bh); return(0); } else { /* XXX - bounds checking */ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("ufs_lookup: wanted (%s,%d) got (%s,%d)\n", name, len, d->d_name, d->d_namlen); } } d = (struct ufs_direct *)((char *)d + d->d_reclen); } brelse(bh); } return(-ENOENT); }
/* * This is blatantly stolen from ext2fs */ static int ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; int error = 0; unsigned long offset, lblk, blk; int i, stored; struct buffer_head * bh; struct ufs_dir_entry * de; struct super_block * sb; int de_reclen; unsigned flags, swab; sb = inode->i_sb; swab = sb->u.ufs_sb.s_swab; flags = sb->u.ufs_sb.s_flags; UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos)) stored = 0; bh = NULL; offset = filp->f_pos & (sb->s_blocksize - 1); while (!error && !stored && filp->f_pos < inode->i_size) { lblk = (filp->f_pos) >> sb->s_blocksize_bits; /* XXX - ufs_bmap() call needs error checking */ blk = ufs_bmap(inode, lblk); bh = bread (sb->s_dev, blk, sb->s_blocksize); if (!bh) { /* XXX - error - skip to the next block */ printk("ufs_readdir: " "dir inode %lu has a hole at offset %lu\n", inode->i_ino, (unsigned long int)filp->f_pos); filp->f_pos += sb->s_blocksize - offset; continue; } revalidate: /* If the dir block has changed since the last call to * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { de = (struct ufs_dir_entry *)(bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at * least that it is non-zero. A * failure will be detected in the * dirent test below. */ de_reclen = SWAB16(de->d_reclen); if (de_reclen < 1) break; i += de_reclen; } offset = i; filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | offset; filp->f_version = inode->i_version; } while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ufs_dir_entry *) (bh->b_data + offset); /* XXX - put in a real ufs_check_dir_entry() */ if ((de->d_reclen == 0) || (ufs_get_de_namlen(de) == 0)) { /* SWAB16() was unneeded -- compare to 0 */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse(bh); return stored; } if (!ufs_check_dir_entry ("ufs_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; } offset += SWAB16(de->d_reclen); if (de->d_ino) { /* SWAB16() was unneeded -- compare to 0 */ /* We might block in the next section * if the data destination is * currently swapped out. So, use a * version stamp to detect whether or * not the directory has been modified * during the copy operation. */ unsigned long version = inode->i_version; UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino))) UFSD(("namlen %u\n", ufs_get_de_namlen(de))) error = filldir(dirent, de->d_name, ufs_get_de_namlen(de), filp->f_pos, SWAB32(de->d_ino)); if (error) break; if (version != inode->i_version) goto revalidate; stored ++; } filp->f_pos += SWAB16(de->d_reclen); } offset = 0; brelse (bh); } UPDATE_ATIME(inode); return 0; }
int ufs_dir_iterate(uufsd_t *ufs, ino_t dirino, int (*func)( struct direct *dirent, int n, char *buf, void *priv_data), void *priv_data) { int i, ret = 0; ufs2_daddr_t ndb; ufs2_daddr_t blkno; int blksize = ufs->d_fs.fs_bsize; u_int64_t dir_size; char *dirbuf = NULL; struct ufs_vnode *vnode; vnode = vnode_get(ufs, dirino); if (vnode == NULL) { ret = -ENOMEM; goto out; } dir_size = vnode2inode(vnode)->i_size; dirbuf = malloc(blksize); if (!dirbuf) { ret = -ENOMEM; goto out; } ndb = howmany(dir_size, ufs->d_fs.fs_bsize); int offset, pos = 0; for (i = 0; i < ndb ; i++) { ret = ufs_bmap(ufs, vnode, i, &blkno); if (ret) { ret = -EIO; goto out; } blksize = ufs_inode_io_size(vnode2inode(vnode), pos, 0); if (blkread(ufs, fsbtodb(&ufs->d_fs, blkno), dirbuf, blksize) == -1) { debugf("Unable to read block %d\n",blkno); ret = -EIO; goto out; } offset = 0; while (offset < blksize && pos + offset < dir_size) { struct direct *de = (struct direct *)(dirbuf + offset); /* HACK: Restrict frame for func() operations * to blocks of DIRBLKSIZ bytes */ int blockoff = offset % DIRBLKSIZ; char * dirblock = dirbuf + (offset-blockoff); ret = (*func)(de, blockoff, dirblock, priv_data); if (ret & DIRENT_CHANGED) { if (blkwrite(ufs, fsbtodb(&ufs->d_fs, blkno), dirbuf, blksize) == -1) { debugf("Unable to write block %d\n",blkno); ret = -EIO; goto out; } } if (ret & DIRENT_ABORT) { ret = 0; goto out; } offset += de->d_reclen; } pos += blksize; } out: if (vnode) { vnode_put(vnode, 0); } if (dirbuf) { free(dirbuf); } return ret; }
/* * This is blatantly stolen from ext2fs */ static int ufs_readdir (struct inode * inode, struct file * filp, void * dirent, filldir_t filldir) { int error = 0; unsigned long offset, lblk, blk; int i, stored; struct buffer_head * bh; struct ufs_direct * de; struct super_block * sb; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("ufs_readdir: ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos); ufs_print_inode(inode); } stored = 0; bh = NULL; offset = filp->f_pos & (sb->s_blocksize - 1); while (!error && !stored && filp->f_pos < inode->i_size) { lblk = (filp->f_pos) >> sb->s_blocksize_bits; blk = ufs_bmap(inode, lblk); /* XXX - ufs_bmap() call needs error checking */ blk = ufs_bmap(inode, lblk); bh = bread (sb->s_dev, blk, sb->s_blocksize); if (!bh) { /* XXX - error - skip to the next block */ printk("ufs_readdir: dir inode %lu has a hole at offset %lu\n", inode->i_ino, (unsigned long int)filp->f_pos); filp->f_pos += sb->s_blocksize - offset; continue; } revalidate: /* If the dir block has changed since the last call to * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { de = (struct ufs_direct *) (bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at * least that it is non-zero. A * failure will be detected in the * dirent test below. */ if (de->d_reclen < 1) break; i += de->d_reclen; } offset = i; filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | offset; filp->f_version = inode->i_version; } while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ufs_direct *) (bh->b_data + offset); /* XXX - put in a real ufs_check_dir_entry() */ if ((de->d_reclen == 0) || (de->d_namlen == 0)) { filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse(bh); return stored; } #if 0 if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; } #endif /* XXX */ offset += de->d_reclen; if (de->d_ino) { /* We might block in the next section * if the data destination is * currently swapped out. So, use a * version stamp to detect whether or * not the directory has been modified * during the copy operation. */ unsigned long version; dcache_add(inode, de->d_name, de->d_namlen, de->d_ino); version = inode->i_version; if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) { printk("ufs_readdir: filldir(%s,%u)\n", de->d_name, de->d_ino); } error = filldir(dirent, de->d_name, de->d_namlen, filp->f_pos, de->d_ino); if (error) break; if (version != inode->i_version) goto revalidate; stored ++; } filp->f_pos += de->d_reclen; } offset = 0; brelse (bh); } #if 0 /* XXX */ if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } #endif /* XXX */ return 0; }