/*
|
/*
|
* linux/fs/ufs/ufs_symlink.c
|
* linux/fs/ufs/ufs_symlink.c
|
*
|
*
|
* Copyright (C) 1996
|
* Copyright (C) 1996
|
* Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
|
* Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
|
* Laboratory for Computer Science Research Computing Facility
|
* Laboratory for Computer Science Research Computing Facility
|
* Rutgers, The State University of New Jersey
|
* Rutgers, The State University of New Jersey
|
*
|
*
|
* $Id: ufs_symlink.c,v 1.1.1.1 2001-09-10 07:44:40 simons Exp $
|
* $Id: ufs_symlink.c,v 1.1.1.1 2001-09-10 07:44:40 simons Exp $
|
*
|
*
|
*/
|
*/
|
|
|
#include <linux/fs.h>
|
#include <linux/fs.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
extern int ufs_bmap (struct inode *, int);
|
extern int ufs_bmap (struct inode *, int);
|
|
|
static int
|
static int
|
ufs_readlink(struct inode * inode, char * buffer, int buflen)
|
ufs_readlink(struct inode * inode, char * buffer, int buflen)
|
{
|
{
|
unsigned long int block;
|
unsigned long int block;
|
struct buffer_head * bh = NULL;
|
struct buffer_head * bh = NULL;
|
char * link;
|
char * link;
|
int i;
|
int i;
|
char c;
|
char c;
|
|
|
if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) {
|
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",
|
printk("ufs_readlink: called on ino %lu dev %u/%u\n",
|
inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
|
inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
|
}
|
}
|
|
|
if (!S_ISLNK(inode->i_mode)) {
|
if (!S_ISLNK(inode->i_mode)) {
|
iput (inode);
|
iput (inode);
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
if (buflen > inode->i_sb->s_blocksize - 1)
|
if (buflen > inode->i_sb->s_blocksize - 1)
|
buflen = inode->i_sb->s_blocksize - 1;
|
buflen = inode->i_sb->s_blocksize - 1;
|
if (inode->i_blocks) {
|
if (inode->i_blocks) {
|
/* XXX - error checking */
|
/* XXX - error checking */
|
block = ufs_bmap(inode, 0);
|
block = ufs_bmap(inode, 0);
|
if (inode->i_sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) {
|
if (inode->i_sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) {
|
printk("ufs_readlink: bmap got %lu for ino %lu\n",
|
printk("ufs_readlink: bmap got %lu for ino %lu\n",
|
block, inode->i_ino);
|
block, inode->i_ino);
|
}
|
}
|
bh = bread(inode->i_dev, block, BLOCK_SIZE);
|
bh = bread(inode->i_dev, block, BLOCK_SIZE);
|
if (!bh) {
|
if (!bh) {
|
iput (inode);
|
iput (inode);
|
printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n",
|
printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n",
|
inode->i_ino, MAJOR(inode->i_dev),
|
inode->i_ino, MAJOR(inode->i_dev),
|
MINOR(inode->i_dev));
|
MINOR(inode->i_dev));
|
return 0;
|
return 0;
|
}
|
}
|
link = bh->b_data;
|
link = bh->b_data;
|
}
|
}
|
else {
|
else {
|
link = (char *)&(inode->u.ufs_i.ui_db[0]);
|
link = (char *)&(inode->u.ufs_i.ui_db[0]);
|
}
|
}
|
i = 0;
|
i = 0;
|
while (i < buflen && (c = link[i])) {
|
while (i < buflen && (c = link[i])) {
|
i++;
|
i++;
|
put_user (c, buffer++);
|
put_user (c, buffer++);
|
}
|
}
|
iput (inode);
|
iput (inode);
|
if (bh)
|
if (bh)
|
brelse (bh);
|
brelse (bh);
|
return i;
|
return i;
|
}
|
}
|
|
|
/*
|
/*
|
* XXX - blatantly stolen from ext2fs
|
* XXX - blatantly stolen from ext2fs
|
*/
|
*/
|
static int
|
static int
|
ufs_follow_link(struct inode * dir, struct inode * inode,
|
ufs_follow_link(struct inode * dir, struct inode * inode,
|
int flag, int mode, struct inode ** res_inode)
|
int flag, int mode, struct inode ** res_inode)
|
{
|
{
|
unsigned long int block;
|
unsigned long int block;
|
int error;
|
int error;
|
struct buffer_head * bh;
|
struct buffer_head * bh;
|
char * link;
|
char * link;
|
|
|
bh = NULL;
|
bh = NULL;
|
|
|
if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) {
|
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",
|
printk("ufs_follow_link: called on ino %lu dev %u/%u\n",
|
dir->i_ino, MAJOR(dir->i_dev), MINOR(dir->i_dev));
|
dir->i_ino, MAJOR(dir->i_dev), MINOR(dir->i_dev));
|
}
|
}
|
|
|
*res_inode = NULL;
|
*res_inode = NULL;
|
if (!dir) {
|
if (!dir) {
|
dir = current->fs->root;
|
dir = current->fs->root;
|
dir->i_count++;
|
dir->i_count++;
|
}
|
}
|
if (!inode) {
|
if (!inode) {
|
iput (dir);
|
iput (dir);
|
return -ENOENT;
|
return -ENOENT;
|
}
|
}
|
if (!S_ISLNK(inode->i_mode)) {
|
if (!S_ISLNK(inode->i_mode)) {
|
iput (dir);
|
iput (dir);
|
*res_inode = inode;
|
*res_inode = inode;
|
return 0;
|
return 0;
|
}
|
}
|
if (current->link_count > 5) {
|
if (current->link_count > 5) {
|
iput (dir);
|
iput (dir);
|
iput (inode);
|
iput (inode);
|
return -ELOOP;
|
return -ELOOP;
|
}
|
}
|
if (inode->i_blocks) {
|
if (inode->i_blocks) {
|
/* read the link from disk */
|
/* read the link from disk */
|
/* XXX - error checking */
|
/* XXX - error checking */
|
block = ufs_bmap(inode, 0);
|
block = ufs_bmap(inode, 0);
|
bh = bread(inode->i_dev, block, BLOCK_SIZE);
|
bh = bread(inode->i_dev, block, BLOCK_SIZE);
|
if (bh == NULL) {
|
if (bh == NULL) {
|
printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n",
|
printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n",
|
inode->i_ino, MAJOR(inode->i_dev),
|
inode->i_ino, MAJOR(inode->i_dev),
|
MINOR(inode->i_dev));
|
MINOR(inode->i_dev));
|
iput(dir);
|
iput(dir);
|
iput(inode);
|
iput(inode);
|
return(-EIO);
|
return(-EIO);
|
}
|
}
|
link = bh->b_data;
|
link = bh->b_data;
|
} else {
|
} else {
|
/* fast symlink */
|
/* fast symlink */
|
link = (char *)&(inode->u.ufs_i.ui_db[0]);
|
link = (char *)&(inode->u.ufs_i.ui_db[0]);
|
}
|
}
|
current->link_count++;
|
current->link_count++;
|
error = open_namei (link, flag, mode, res_inode, dir);
|
error = open_namei (link, flag, mode, res_inode, dir);
|
current->link_count--;
|
current->link_count--;
|
iput (inode);
|
iput (inode);
|
if (bh) {
|
if (bh) {
|
brelse (bh);
|
brelse (bh);
|
}
|
}
|
return(error);
|
return(error);
|
}
|
}
|
|
|
|
|
static struct file_operations ufs_symlink_operations = {
|
static struct file_operations ufs_symlink_operations = {
|
NULL, /* lseek */
|
NULL, /* lseek */
|
NULL, /* read */
|
NULL, /* read */
|
NULL, /* write */
|
NULL, /* write */
|
NULL, /* readdir */
|
NULL, /* readdir */
|
NULL, /* select */
|
NULL, /* select */
|
NULL, /* ioctl */
|
NULL, /* ioctl */
|
NULL, /* mmap */
|
NULL, /* mmap */
|
NULL, /* open */
|
NULL, /* open */
|
NULL, /* release */
|
NULL, /* release */
|
NULL, /* fsync */ /* XXX - is this ok? */
|
NULL, /* fsync */ /* XXX - is this ok? */
|
NULL, /* fasync */
|
NULL, /* fasync */
|
NULL, /* check_media_change */
|
NULL, /* check_media_change */
|
NULL, /* revalidate */
|
NULL, /* revalidate */
|
};
|
};
|
|
|
struct inode_operations ufs_symlink_inode_operations = {
|
struct inode_operations ufs_symlink_inode_operations = {
|
&ufs_symlink_operations, /* default directory file operations */
|
&ufs_symlink_operations, /* default directory file operations */
|
NULL, /* create */
|
NULL, /* create */
|
NULL, /* lookup */
|
NULL, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
&ufs_readlink, /* readlink */
|
&ufs_readlink, /* readlink */
|
&ufs_follow_link, /* follow_link */
|
&ufs_follow_link, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL, /* permission */
|
NULL, /* permission */
|
NULL, /* smap */
|
NULL, /* smap */
|
};
|
};
|
|
|
/*
|
/*
|
* Local Variables: ***
|
* Local Variables: ***
|
* c-indent-level: 8 ***
|
* c-indent-level: 8 ***
|
* c-continued-statement-offset: 8 ***
|
* c-continued-statement-offset: 8 ***
|
* c-brace-offset: -8 ***
|
* c-brace-offset: -8 ***
|
* c-argdecl-indent: 0 ***
|
* c-argdecl-indent: 0 ***
|
* c-label-offset: -8 ***
|
* c-label-offset: -8 ***
|
* End: ***
|
* End: ***
|
*/
|
*/
|
|
|